├── .gitattributes ├── .gitignore ├── Mors.Journeys.Application.Client.Web ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.vue │ ├── Calendar.vue │ ├── JourneyCalendar.vue │ ├── PassengerList.vue │ ├── api.js │ ├── main.js │ └── main.scss └── vite.config.js ├── Mors.Journeys.Application.Client.Wpf ├── Bootstrapper.cs ├── Commands │ └── StoreJourneyTemplatesCommand.cs ├── Components │ ├── Calendar │ │ ├── CalendarDay.cs │ │ ├── CalendarDayCollection.cs │ │ └── CalendarMonth.cs │ ├── Notifications │ │ ├── ErrorNotification.cs │ │ ├── NotifierControl.cs │ │ ├── NotifierControl.xaml │ │ ├── NotifierViewModel.cs │ │ └── SuccessNotification.cs │ ├── Popups │ │ ├── TogglePopup.cs │ │ └── TogglePopup.xaml │ └── Settings │ │ ├── SettingSelector.cs │ │ ├── SettingSelector.xaml │ │ └── SettingsCommands.cs ├── Events │ └── JourneyWithLiftsAddedEvent.cs ├── Features │ ├── AddJourneysWithLifts │ │ ├── AddJourneyWithLiftsControl.cs │ │ ├── AddJourneyWithLiftsControl.xaml │ │ ├── AddJourneyWithLiftsViewModel.cs │ │ └── LiftViewModel.cs │ ├── CalculatePassengerLiftsCostInPeriod │ │ ├── CalculatePassengerLiftsCostInPeriodControl.cs │ │ ├── CalculatePassengerLiftsCostInPeriodControl.xaml │ │ └── CalculatePassengerLiftsCostInPeriodViewModel.cs │ └── ShowJourneysInCalendar │ │ ├── CalendarContentProvider.cs │ │ ├── JourneyCalendarsControl.cs │ │ ├── JourneyCalendarsControl.xaml │ │ ├── JourneyCalendarsViewModel.cs │ │ ├── JourneyDaySummary.cs │ │ ├── Month.cs │ │ ├── MonthSelector.cs │ │ ├── Passenger.cs │ │ └── PassengerLiftCalendar.cs ├── ICommandDispatcher.cs ├── ICommandHandlerRegistry.cs ├── IEventBus.cs ├── IIdFactory.cs ├── IQueryDispatcher.cs ├── IQueryHandlerRegistry.cs ├── Infrastructure │ ├── DelegateCommand.cs │ ├── DelegateCommand`1.cs │ ├── Extensions │ │ ├── EventExtensions.cs │ │ ├── NotifyCollectionChangedEventExtensions.cs │ │ └── PropertyChangedEventExtensions.cs │ └── Interfaces │ │ └── INotifyCollectionChangedReadOnlyList.cs ├── MainPanel.xaml ├── MainPanel.xaml.cs ├── Mors.Journeys.Application.Client.Wpf.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Queries │ └── GetJourneyTemplatesQuery.cs ├── Resources.xaml ├── Settings │ ├── JourneyTemplate.cs │ ├── JourneyTemplateCollection.cs │ ├── LiftTemplate.cs │ └── Settings.cs └── app.config ├── Mors.Journeys.Application ├── Bootstrapper.cs ├── CommandHandlers │ └── AddJourneyWithLiftsCommandHandler.cs ├── EventReplayers │ ├── JourneyCreatedEventReplayer.cs │ ├── LiftAddedEventReplayer.cs │ └── PersonCreatedEventReplayer.cs ├── ICommandHandlerRegistry.cs ├── IEventBus.cs ├── IEventSourcing.cs ├── IQueryDispatcher.cs ├── IQueryHandlerRegistry.cs ├── IRepositories.cs ├── Mors.Journeys.Application.csproj └── QueryHandlers │ ├── Infrastructure │ ├── Period.cs │ └── Views │ │ ├── IMaybe.cs │ │ ├── Just.cs │ │ ├── Nothing.cs │ │ ├── ObjectExtensions.cs │ │ ├── ValueLookup.cs │ │ ├── ValueMultiSet.cs │ │ └── ValueSet.cs │ ├── JourneyView.cs │ ├── JourneysByPassengerThenMonthThenDayView.cs │ ├── Messages │ ├── FailureMessages.Designer.cs │ └── FailureMessages.resx │ ├── PassengerLiftCostCalculator.cs │ └── PersonView.cs ├── Mors.Journeys.Data ├── Commands │ ├── AddJourneyWithLiftsCommand.cs │ └── Dtos │ │ └── Lift.cs ├── Events │ ├── JourneyCreatedEvent.cs │ ├── LiftAddedEvent.cs │ └── PersonCreatedEvent.cs ├── IHasId.cs ├── IQuery.cs ├── Mors.Journeys.Data.csproj └── Queries │ ├── Dtos │ ├── Journey.cs │ ├── JourneyWithLift.cs │ ├── JourneysByPassengerThenMonthThenDay │ │ ├── Day.cs │ │ ├── Fact.cs │ │ ├── Key.cs │ │ ├── Month.cs │ │ ├── Passenger.cs │ │ └── Value.cs │ ├── JourneysOnDay.cs │ ├── Lift.cs │ ├── PassengerLiftsCost.cs │ ├── Period.cs │ └── PersonName.cs │ ├── GetCostOfPassengerLiftsInPeriodQuery.cs │ ├── GetJourneysByPassengerThenMonthThenDayQuery.cs │ ├── GetJourneysInPeriodQuery.cs │ ├── GetPeopleNamesQuery.cs │ └── GetPersonIdByNameQuery.cs ├── Mors.Journeys.Domain.Expenses.Test ├── Capabilities │ ├── ExpenseListTest.cs │ ├── LiftIdTest.cs │ └── MoneyTest.cs └── Mors.Journeys.Domain.Expenses.Test.csproj ├── Mors.Journeys.Domain.Expenses ├── Capabilities │ ├── Distance.cs │ ├── Expense.cs │ ├── ExpenseList.cs │ ├── IJourneyEvent.cs │ ├── IJourneyVisitor.cs │ ├── Journey.cs │ ├── Journeys │ │ ├── Events │ │ │ ├── Drive.cs │ │ │ ├── JourneyFinish.cs │ │ │ ├── JourneyStart.cs │ │ │ ├── PassengerExit.cs │ │ │ └── PassengerPickup.cs │ │ ├── Route.cs │ │ ├── RouteDistance.cs │ │ └── RoutePoint.cs │ ├── Lift.cs │ ├── LiftId.cs │ └── Money.cs ├── Messages.Designer.cs ├── Messages.resx ├── Mors.Journeys.Domain.Expenses.csproj ├── Operations │ ├── Clerk.cs │ ├── JourneyBuilder.cs │ └── JourneyFactory.cs ├── Policies │ ├── EquallyDistributedCostPolicy.cs │ └── IJourneyCostPolicy.cs └── Properties │ └── AssemblyInfo.cs ├── Mors.Journeys.Domain.Infrastructure ├── Collections │ └── ImmutableList.cs ├── Exceptions │ └── InvariantViolationException.cs ├── Markers │ ├── AggregateAttribute.cs │ ├── EntityAttribute.cs │ ├── FactoryAttribute.cs │ ├── PolicyAttribute.cs │ ├── RepositoryAttribute.cs │ ├── ServiceAttribute.cs │ └── ValueObjectAttribute.cs └── Mors.Journeys.Domain.Infrastructure.csproj ├── Mors.Journeys.Domain.Journeys.Test ├── Capabilities │ ├── DistanceTest.cs │ └── LiftTest.cs ├── Mors.Journeys.Domain.Journeys.Test.csproj └── Operations │ └── JourneyTest.cs ├── Mors.Journeys.Domain.Journeys ├── Capabilities │ ├── Distance.cs │ ├── DistanceUnit.cs │ └── Lift.cs ├── Messages.Designer.cs ├── Messages.resx ├── Mors.Journeys.Domain.Journeys.csproj ├── Operations │ └── Journey.cs └── Properties │ └── AssemblyInfo.cs ├── Mors.Journeys.Domain.People.Test ├── Mors.Journeys.Domain.People.Test.csproj └── PersonTest.cs ├── Mors.Journeys.Domain.People ├── Messages.Designer.cs ├── Messages.resx ├── Mors.Journeys.Domain.People.csproj └── Person.cs ├── Mors.Journeys.Domain.Test.Infrastructure ├── EventBusMock.cs ├── EventMatcher.cs ├── Id.cs └── Mors.Journeys.Domain.Test.Infrastructure.csproj └── Mors.Journeys.sln /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | node_modules/ 5 | .vscode/ 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.sln.docstates 11 | 12 | # Build results 13 | 14 | [Dd]ebug/ 15 | [Rr]elease/ 16 | x64/ 17 | build/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | *.pubxml 98 | 99 | # NuGet Packages Directory 100 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 101 | packages/ 102 | 103 | # Windows Azure Build Output 104 | csx 105 | *.build.csdef 106 | 107 | # Windows Store app package directory 108 | AppPackages/ 109 | 110 | # Others 111 | sql/ 112 | *.Cache 113 | ClientBin/ 114 | [Ss]tyle[Cc]op.* 115 | ~$* 116 | *~ 117 | *.dbmdl 118 | *.[Pp]ublish.xml 119 | *.pfx 120 | *.publishsettings 121 | 122 | # RIA/Silverlight projects 123 | Generated_Code/ 124 | 125 | # Backup & report files from converting an old project file to a newer 126 | # Visual Studio version. Backup files are not needed, because we have git ;-) 127 | _UpgradeReport_Files/ 128 | Backup*/ 129 | UpgradeLog*.XML 130 | UpgradeLog*.htm 131 | 132 | # SQL Server files 133 | App_Data/*.mdf 134 | App_Data/*.ldf 135 | 136 | 137 | #LightSwitch generated files 138 | GeneratedArtifacts/ 139 | _Pvt_Extensions/ 140 | ModelManifest.xml 141 | 142 | # ========================= 143 | # Windows detritus 144 | # ========================= 145 | 146 | # Windows image file caches 147 | Thumbs.db 148 | ehthumbs.db 149 | 150 | # Folder config file 151 | Desktop.ini 152 | 153 | # Recycle Bin used on file shares 154 | $RECYCLE.BIN/ 155 | 156 | # Mac desktop service store files 157 | .DS_Store 158 | 159 | *.g.cs 160 | *.g.i.cs 161 | *.g.resources 162 | *.resources 163 | *.lref 164 | *.csproj.FileListAbsolute.txt 165 | *.baml 166 | TemporaryGeneratedFile_*.cs 167 | *.exe 168 | *.dll 169 | /Journeys.Hosting.Service/data 170 | *.ide 171 | *.nupkg 172 | /.vs 173 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Journeys 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "journeys", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "bootstrap": "^5.2.3", 11 | "sass": "^1.56.1", 12 | "vue": "^3.2.41" 13 | }, 14 | "devDependencies": { 15 | "@vitejs/plugin-vue": "^3.1.2", 16 | "vite": "^3.1.8" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 26 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/Calendar.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 73 | 74 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/JourneyCalendar.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 64 | 65 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/PassengerList.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/api.js: -------------------------------------------------------------------------------- 1 | const queryUrl = `${__API_URL__}/api/query` 2 | 3 | export async function journeys() { 4 | const response = await fetch(queryUrl, { 5 | method: 'POST', 6 | headers: { 7 | 'Accept': 'application/json', 8 | 'Content-Type': 'application/json' 9 | }, 10 | body: JSON.stringify({ "$type": "Mors.Journeys.Data.Queries.GetJourneysByPassengerThenMonthThenDayQuery, Mors.Journeys.Data" }) 11 | }); 12 | const facts = await response.json(); 13 | const journeys = 14 | facts.reduce((journeys, fact) => { 15 | const passengerId = fact.Key.Passenger.Id; 16 | const year = fact.Key.Month.Year; 17 | const month = fact.Key.Month.MonthOfYear; 18 | const day = fact.Key.Day.DayOfMonth; 19 | const values = { 20 | liftDistance: fact.Value.LiftDistance, 21 | liftCount: fact.Value.LiftCount, 22 | journeyDistance: fact.Value.JourneyDistance, 23 | journeyCount: fact.Value.JourneyCount 24 | }; 25 | let journeysOfPassenger = journeys[passengerId]; 26 | if (journeysOfPassenger === undefined) { 27 | journeysOfPassenger = {}; 28 | journeys[passengerId] = journeysOfPassenger; 29 | } 30 | let journeysOfYear = journeysOfPassenger[year]; 31 | if (journeysOfYear === undefined) { 32 | journeysOfYear = {}; 33 | journeysOfPassenger[year] = journeysOfYear; 34 | } 35 | let journeysOfMonth = journeysOfYear[month]; 36 | if (journeysOfMonth === undefined) { 37 | journeysOfMonth = {}; 38 | journeysOfYear[month] = journeysOfMonth; 39 | } 40 | journeysOfMonth[day] = values; 41 | return journeys; 42 | }, 43 | {}); 44 | return journeys; 45 | } 46 | 47 | export async function passengers() { 48 | const response = await fetch(queryUrl, { 49 | method: 'POST', 50 | headers: { 51 | 'Accept': 'application/json', 52 | 'Content-Type': 'application/json' 53 | }, 54 | body: JSON.stringify({ "$type": "Mors.Journeys.Data.Queries.GetPeopleNamesQuery, Mors.Journeys.Data" }) 55 | }); 56 | const passengers = await response.json(); 57 | passengers.sort((a, b) => { a.Name.localeCompare(b.Name) }) 58 | return passengers.map(x => ({ name: x.Name, id: x.OwnerId })); 59 | } -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import './main.scss' 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/src/main.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap"; -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Web/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | export default defineConfig(env => ({ 7 | base: env.command == "build" ? "/sites/journeys/" : "", 8 | define: { "__API_URL__": JSON.stringify(env.command == "build" ? "http://192.168.0.213:65363" : "http://localhost:65363") }, 9 | plugins: [vue()], 10 | resolve: { 11 | alias: { 12 | '@': fileURLToPath(new URL('./src', import.meta.url)), 13 | '~bootstrap': fileURLToPath(new URL('./node_modules/bootstrap', import.meta.url)), 14 | } 15 | } 16 | }) 17 | ) 18 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Bootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Mors.Journeys.Application.Client.Wpf.Commands; 3 | using Mors.Journeys.Application.Client.Wpf.Queries; 4 | using Mors.Journeys.Application.Client.Wpf.Settings; 5 | using System.Windows; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf 8 | { 9 | public sealed class Bootstrapper 10 | { 11 | private readonly IEventBus _eventBus; 12 | private readonly ICommandDispatcher _commandDispatcher; 13 | private readonly IQueryDispatcher _queryDispatcher; 14 | private readonly IIdFactory _idFactory; 15 | private readonly IQueryHandlerRegistry _queryHandlerRegistry; 16 | private readonly ICommandHandlerRegistry _commandHandlerRegistry; 17 | 18 | public Bootstrapper( 19 | IEventBus eventBus, 20 | ICommandDispatcher commandDispatcher, 21 | ICommandHandlerRegistry commandHandlerRegistry, 22 | IQueryDispatcher queryDispatcher, 23 | IQueryHandlerRegistry queryHandlerRegistry, 24 | IIdFactory idFactory) 25 | { 26 | _eventBus = eventBus; 27 | _commandDispatcher = commandDispatcher; 28 | _commandHandlerRegistry = commandHandlerRegistry; 29 | _queryDispatcher = queryDispatcher; 30 | _queryHandlerRegistry = queryHandlerRegistry; 31 | _idFactory = idFactory; 32 | } 33 | 34 | public UIElement Bootstrap() 35 | { 36 | _queryHandlerRegistry.SetHandler>(Settings.Settings.Default.Execute); 37 | _commandHandlerRegistry.SetHandler(Settings.Settings.Default.Handle); 38 | return new MainPanel( 39 | _commandDispatcher, 40 | _queryDispatcher, 41 | _eventBus, 42 | _idFactory); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Commands/StoreJourneyTemplatesCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mors.Journeys.Application.Client.Wpf.Settings; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Commands 6 | { 7 | internal sealed class StoreJourneyTemplatesCommand 8 | { 9 | public StoreJourneyTemplatesCommand( 10 | IEnumerable addedTemplates, 11 | IEnumerable removedTemplateNames) 12 | { 13 | AddedTemplates = addedTemplates.ToList(); 14 | RemovedTemplateNames = removedTemplateNames.ToList(); 15 | } 16 | 17 | public List AddedTemplates { get; private set; } 18 | 19 | public List RemovedTemplateNames { get; private set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Calendar/CalendarDay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 5 | 6 | namespace Mors.Journeys.Application.Client.Wpf.Components.Calendar 7 | { 8 | internal sealed class CalendarDay : INotifyPropertyChanged 9 | { 10 | private readonly int _dayOfMonth; 11 | private object _content; 12 | private DateTime _date; 13 | 14 | public CalendarDay(int dayOfMonth) 15 | { 16 | _dayOfMonth = dayOfMonth; 17 | } 18 | 19 | public void Clear() 20 | { 21 | Content = null; 22 | } 23 | 24 | public void Change(int year, int month) 25 | { 26 | Date = new DateTime(year, month, _dayOfMonth); 27 | PropertyChanged.Raise(this, () => DayOfWeekIndex); 28 | PropertyChanged.Raise(this, () => WeekOfMonthIndex); 29 | } 30 | 31 | public void Fill(object content) 32 | { 33 | Content = content; 34 | } 35 | 36 | public object Content 37 | { 38 | get { return _content; } 39 | private set 40 | { 41 | _content = value; 42 | PropertyChanged.Raise(this); 43 | } 44 | } 45 | 46 | public int DayOfMonth 47 | { 48 | get { return _dayOfMonth; } 49 | } 50 | 51 | public int DayOfWeekIndex 52 | { 53 | get 54 | { 55 | var dayOfWeek = CultureInfo.CurrentCulture.Calendar.GetDayOfWeek(_date); 56 | return dayOfWeek == DayOfWeek.Sunday 57 | ? 6 58 | : (int)dayOfWeek - 1; 59 | } 60 | } 61 | 62 | public int WeekOfMonthIndex 63 | { 64 | get 65 | { 66 | var weekOfMonthFirst = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(new DateTime(_date.Year, _date.Month, 1), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); 67 | var weekOfCurrent = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(_date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); 68 | return weekOfCurrent < weekOfMonthFirst 69 | ? weekOfCurrent 70 | : weekOfCurrent - weekOfMonthFirst; 71 | } 72 | } 73 | 74 | public DateTime Date 75 | { 76 | get { return _date; } 77 | private set 78 | { 79 | _date = value; 80 | PropertyChanged.Raise(this); 81 | } 82 | } 83 | 84 | public event PropertyChangedEventHandler PropertyChanged; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Calendar/CalendarDayCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Specialized; 5 | using System.Globalization; 6 | using System.Linq; 7 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 8 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Interfaces; 9 | 10 | namespace Mors.Journeys.Application.Client.Wpf.Components.Calendar 11 | { 12 | internal sealed class CalendarDayCollection : INotifyCollectionChangedReadOnlyList 13 | { 14 | private const int AvailableDaysCount = 31; 15 | private readonly CalendarDay[] _availableDays; 16 | private int _dayCount; 17 | 18 | public CalendarDayCollection() 19 | { 20 | _availableDays = Enumerable.Range(1, AvailableDaysCount).Select(dayInMonth => new CalendarDay(dayInMonth)).ToArray(); 21 | _dayCount = 0; 22 | } 23 | 24 | public void Change(int year, int monthOfYear) 25 | { 26 | var newDayCount = GetNewDayCount(year, monthOfYear); 27 | var oldDayCount = _dayCount; 28 | _dayCount = newDayCount; 29 | foreach (var day in this) 30 | { 31 | day.Change(year, monthOfYear); 32 | } 33 | var dayCountDifference = Math.Abs(newDayCount - oldDayCount); 34 | if (oldDayCount < newDayCount) 35 | { 36 | var addedItems = new CalendarDay[dayCountDifference]; 37 | Array.Copy(_availableDays, oldDayCount, addedItems, 0, dayCountDifference); 38 | CollectionChanged.Raise(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedItems, oldDayCount)); 39 | } 40 | if (oldDayCount > newDayCount) 41 | { 42 | var removedItems = new CalendarDay[dayCountDifference]; 43 | Array.Copy(_availableDays, newDayCount, removedItems, 0, dayCountDifference); 44 | CollectionChanged.Raise(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, newDayCount)); 45 | } 46 | } 47 | 48 | public void Clear() 49 | { 50 | foreach (var day in this) 51 | { 52 | day.Clear(); 53 | } 54 | } 55 | 56 | public void Fill(Func contentProvider) 57 | { 58 | foreach (var day in this) 59 | { 60 | var content = contentProvider(day.DayOfMonth); 61 | day.Fill(content); 62 | } 63 | } 64 | 65 | private static int GetNewDayCount(int year, int monthOfYear) 66 | { 67 | try 68 | { 69 | return Math.Min(CultureInfo.CurrentCulture.Calendar.GetDaysInMonth(year, monthOfYear), AvailableDaysCount); 70 | } 71 | catch (ArgumentOutOfRangeException) 72 | { 73 | return 0; 74 | } 75 | } 76 | 77 | public int Count 78 | { 79 | get { return _dayCount; } 80 | } 81 | 82 | public IEnumerator GetEnumerator() 83 | { 84 | for(var idx = 0; idx < _dayCount; ++idx) 85 | { 86 | yield return _availableDays[idx]; 87 | } 88 | } 89 | 90 | IEnumerator IEnumerable.GetEnumerator() 91 | { 92 | return GetEnumerator(); 93 | } 94 | 95 | public event NotifyCollectionChangedEventHandler CollectionChanged; 96 | 97 | public CalendarDay this[int index] 98 | { 99 | get 100 | { 101 | if (index > _dayCount) throw new ArgumentOutOfRangeException("index"); 102 | return _availableDays[index]; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Calendar/CalendarMonth.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 4 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Interfaces; 5 | 6 | namespace Mors.Journeys.Application.Client.Wpf.Components.Calendar 7 | { 8 | internal sealed class CalendarMonth : INotifyPropertyChanged 9 | { 10 | private readonly CalendarDayCollection _days = new CalendarDayCollection(); 11 | private int _monthOfYear; 12 | private int _year; 13 | 14 | public INotifyCollectionChangedReadOnlyList Days { get { return _days; } } 15 | 16 | public int MonthOfYear 17 | { 18 | get { return _monthOfYear; } 19 | set 20 | { 21 | _monthOfYear = value; 22 | PropertyChanged.Raise(this); 23 | } 24 | } 25 | 26 | public int Year 27 | { 28 | get { return _year; } 29 | set 30 | { 31 | _year = value; 32 | PropertyChanged.Raise(this); 33 | } 34 | } 35 | 36 | public void Clear() 37 | { 38 | _days.Clear(); 39 | } 40 | 41 | public void Fill(Func contentProvider) 42 | { 43 | _days.Fill(contentProvider); 44 | } 45 | 46 | public void Change(int year, int monthOfYear) 47 | { 48 | Year = year; 49 | MonthOfYear = monthOfYear; 50 | _days.Change(year, monthOfYear); 51 | } 52 | 53 | public event PropertyChangedEventHandler PropertyChanged; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Notifications/ErrorNotification.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.Client.Wpf.Components.Notifications 2 | { 3 | internal sealed class ErrorNotification 4 | { 5 | public ErrorNotification(string message) 6 | { 7 | Message = message; 8 | } 9 | 10 | public string Message { get; private set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Notifications/NotifierControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Components.Notifications 4 | { 5 | internal sealed class NotifierControl : ItemsControl 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Notifications/NotifierControl.xaml: -------------------------------------------------------------------------------- 1 |  4 | 38 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Notifications/NotifierViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Components.Notifications 4 | { 5 | internal sealed class NotifierViewModel 6 | { 7 | public NotifierViewModel() 8 | { 9 | Items = new ObservableCollection(); 10 | } 11 | 12 | public ObservableCollection Items { get; private set; } 13 | 14 | public void Replace(object notification) 15 | { 16 | Items.Clear(); 17 | Items.Add(notification); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Notifications/SuccessNotification.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.Client.Wpf.Components.Notifications 2 | { 3 | internal sealed class SuccessNotification 4 | { 5 | public SuccessNotification(string message) 6 | { 7 | Message = message; 8 | } 9 | 10 | public string Message { get; private set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Popups/TogglePopup.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Components.Popups 6 | { 7 | [TemplatePart(Name = "PART_Toggle", Type = typeof(ToggleButton))] 8 | internal sealed class TogglePopup : ContentControl 9 | { 10 | public static readonly DependencyProperty ToggleContentProperty = DependencyProperty.Register("ToggleContent", typeof(object), typeof(TogglePopup)); 11 | public static readonly DependencyProperty ToggleContentStringFormatProperty = DependencyProperty.Register("ToggleContentStringFormat", typeof(string), typeof(TogglePopup)); 12 | public static readonly DependencyProperty ToggleContentTemplateProperty = DependencyProperty.Register("ToggleContentTemplate", typeof(DataTemplate), typeof(TogglePopup)); 13 | public static readonly DependencyProperty ToggleContentTemplateSelectorProperty = DependencyProperty.Register("ToggleContentTemplateSelector", typeof(DataTemplateSelector), typeof(TogglePopup)); 14 | 15 | public object ToggleContent 16 | { 17 | get { return GetValue(ToggleContentProperty); } 18 | set { SetValue(ToggleContentProperty, value); } 19 | } 20 | 21 | public string ToggleContentStringFormat 22 | { 23 | get { return (string)GetValue(ToggleContentStringFormatProperty); } 24 | set { SetValue(ToggleContentStringFormatProperty, value); } 25 | } 26 | 27 | public DataTemplate ToggleContentTemplate 28 | { 29 | get { return (DataTemplate)GetValue(ToggleContentTemplateProperty); } 30 | set { SetValue(ToggleContentTemplateProperty, value); } 31 | } 32 | 33 | public DataTemplateSelector ToggleContentTemplateSelector 34 | { 35 | get { return (DataTemplateSelector)GetValue(ToggleContentTemplateSelectorProperty); } 36 | set { SetValue(ToggleContentTemplateSelectorProperty, value); } 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Popups/TogglePopup.xaml: -------------------------------------------------------------------------------- 1 |  4 | 30 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Settings/SettingSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Data; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Components.Settings 8 | { 9 | [TemplatePart(Name = "PART_Selector", Type = typeof(Selector))] 10 | [TemplatePart(Name = "PART_Load", Type = typeof(ButtonBase))] 11 | [TemplatePart(Name = "PART_Save", Type = typeof(ButtonBase))] 12 | [TemplatePart(Name = "PART_Remove", Type = typeof(ButtonBase))] 13 | internal sealed class SettingSelector : Control 14 | { 15 | public static readonly DependencyProperty SettingNamePathProperty = DependencyProperty.Register("SettingNamePath", typeof(string), typeof(SettingSelector)); 16 | 17 | public static readonly DependencyProperty SettingsSourceProperty = DependencyProperty.Register("SettingsSource", typeof(IEnumerable), typeof(SettingSelector)); 18 | 19 | public string SettingNamePath 20 | { 21 | get { return (string)GetValue(SettingNamePathProperty); } 22 | set { SetValue(SettingNamePathProperty, value); } 23 | } 24 | 25 | public IEnumerable SettingsSource 26 | { 27 | get { return (IEnumerable)GetValue(SettingsSourceProperty); } 28 | set { SetValue(SettingsSourceProperty, value); } 29 | } 30 | 31 | public override void OnApplyTemplate() 32 | { 33 | base.OnApplyTemplate(); 34 | var selector = GetTemplateChild("PART_Selector"); 35 | if (selector != null) 36 | { 37 | BindingOperations.SetBinding(selector, ItemsControl.ItemsSourceProperty, new Binding("SettingsSource") { Source = this }); 38 | BindingOperations.SetBinding(selector, Selector.DisplayMemberPathProperty, new Binding("SettingNamePath") { Source = this }); 39 | var loadButton = GetTemplateChild("PART_Load") as ButtonBase; 40 | if (loadButton != null) 41 | { 42 | BindingOperations.SetBinding(loadButton, ButtonBase.CommandParameterProperty, new Binding("Text") { Source = selector }); 43 | loadButton.Command = SettingsCommands.LoadSettingCommand; 44 | } 45 | var saveButton = GetTemplateChild("PART_Save") as ButtonBase; 46 | if (saveButton != null) 47 | { 48 | BindingOperations.SetBinding(saveButton, ButtonBase.CommandParameterProperty, new Binding("Text") { Source = selector }); 49 | saveButton.Command = SettingsCommands.SaveSettingCommand; 50 | } 51 | var removeButton = GetTemplateChild("PART_Remove") as ButtonBase; 52 | if (removeButton != null) 53 | { 54 | BindingOperations.SetBinding(removeButton, ButtonBase.CommandParameterProperty, new Binding("Text") { Source = selector }); 55 | removeButton.Command = SettingsCommands.RemoveSettingCommand; 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Settings/SettingSelector.xaml: -------------------------------------------------------------------------------- 1 |  4 | 33 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Components/Settings/SettingsCommands.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Components.Settings 4 | { 5 | internal static class SettingsCommands 6 | { 7 | public static readonly RoutedCommand LoadSettingCommand = new RoutedCommand("LoadSetting", typeof(SettingsCommands)); 8 | 9 | public static readonly RoutedCommand RemoveSettingCommand = new RoutedCommand("RemoveSetting", typeof(SettingsCommands)); 10 | 11 | public static readonly RoutedCommand SaveSettingCommand = new RoutedCommand("SaveSetting", typeof(SettingsCommands)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Events/JourneyWithLiftsAddedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.Client.Wpf.Events 2 | { 3 | internal sealed class JourneyWithLiftsAddedEvent 4 | { 5 | public object JourneyId { get; private set; } 6 | 7 | public JourneyWithLiftsAddedEvent(object journeyId) 8 | { 9 | JourneyId = journeyId; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/AddJourneysWithLifts/AddJourneyWithLiftsControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | using System.Windows.Data; 5 | using System.Windows.Input; 6 | using Mors.Journeys.Application.Client.Wpf.Components.Settings; 7 | 8 | namespace Mors.Journeys.Application.Client.Wpf.Features.AddJourneysWithLifts 9 | { 10 | internal sealed class AddJourneyWithLiftsControl : Control 11 | { 12 | public AddJourneyWithLiftsControl() 13 | { 14 | BindingGroup = new BindingGroup(); 15 | CommandBindings.Add(new CommandBinding(SettingsCommands.LoadSettingCommand, OnLoadSetting)); 16 | CommandBindings.Add(new CommandBinding(SettingsCommands.SaveSettingCommand, OnSaveSetting)); 17 | CommandBindings.Add(new CommandBinding(SettingsCommands.RemoveSettingCommand, OnRemoveSetting)); 18 | } 19 | 20 | private void OnRemoveSetting(object sender, ExecutedRoutedEventArgs e) 21 | { 22 | var removeSettingCommand = (ICommand)(DataContext as dynamic).RemoveSettingCommand; 23 | removeSettingCommand.Execute(e.Parameter); 24 | } 25 | 26 | private void OnSaveSetting(object sender, ExecutedRoutedEventArgs e) 27 | { 28 | BindingGroup.CommitEdit(); 29 | var saveSettingCommand = (ICommand)(DataContext as dynamic).SaveSettingCommand; 30 | saveSettingCommand.Execute(e.Parameter); 31 | } 32 | 33 | private void OnLoadSetting(object sender, ExecutedRoutedEventArgs e) 34 | { 35 | var loadSettingCommand = (ICommand)(DataContext as dynamic).LoadSettingCommand; 36 | loadSettingCommand.Execute(e.Parameter); 37 | } 38 | 39 | public override void OnApplyTemplate() 40 | { 41 | base.OnApplyTemplate(); 42 | var addButton = GetTemplateChild("AddButton") as ButtonBase; 43 | if (addButton != null) 44 | { 45 | addButton.Click += OnAddButtonClick; 46 | } 47 | } 48 | 49 | private void OnAddButtonClick(object sender, RoutedEventArgs e) 50 | { 51 | BindingGroup.CommitEdit(); 52 | if (BindingGroup.HasValidationError) 53 | return; 54 | var addJourneyCommand = (ICommand)(DataContext as dynamic).AddJourneyCommand; 55 | addJourneyCommand.Execute(null); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/AddJourneysWithLifts/LiftViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 3 | using Mors.Journeys.Data.Commands.Dtos; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Features.AddJourneysWithLifts 6 | { 7 | internal sealed class LiftViewModel : INotifyPropertyChanged 8 | { 9 | private string _passengerName; 10 | private decimal _liftDistance; 11 | 12 | public string PassengerName 13 | { 14 | get { return _passengerName; } 15 | set 16 | { 17 | _passengerName = value; 18 | PropertyChanged.Raise(this); 19 | } 20 | } 21 | 22 | public decimal LiftDistance 23 | { 24 | get { return _liftDistance; } 25 | set 26 | { 27 | _liftDistance = value; 28 | PropertyChanged.Raise(this); 29 | } 30 | } 31 | 32 | public event PropertyChangedEventHandler PropertyChanged; 33 | 34 | public Lift ToDto() 35 | { 36 | return new Lift(PassengerName, LiftDistance); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/CalculatePassengerLiftsCostInPeriod/CalculatePassengerLiftsCostInPeriodControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | using System.Windows.Data; 5 | using System.Windows.Input; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Features.CalculatePassengerLiftsCostInPeriod 8 | { 9 | internal sealed class CalculatePassengerLiftsCostInPeriodControl : Control 10 | { 11 | public CalculatePassengerLiftsCostInPeriodControl() 12 | { 13 | BindingGroup = new BindingGroup(); 14 | } 15 | 16 | public override void OnApplyTemplate() 17 | { 18 | base.OnApplyTemplate(); 19 | var calculateButton = GetTemplateChild("CalculateButton") as ButtonBase; 20 | if (calculateButton != null) 21 | { 22 | calculateButton.Click += OnCalculateButtonClick; 23 | } 24 | } 25 | 26 | private void OnCalculateButtonClick(object sender, RoutedEventArgs e) 27 | { 28 | BindingGroup.CommitEdit(); 29 | if (BindingGroup.HasValidationError) 30 | return; 31 | var calculateCommand = (ICommand)(DataContext as dynamic).CalculateCommand; 32 | calculateCommand.Execute(null); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/CalculatePassengerLiftsCostInPeriod/CalculatePassengerLiftsCostInPeriodControl.xaml: -------------------------------------------------------------------------------- 1 |  6 | 47 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/CalculatePassengerLiftsCostInPeriod/CalculatePassengerLiftsCostInPeriodViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Windows.Input; 6 | using Mors.Journeys.Application.Client.Wpf.Infrastructure; 7 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 8 | using Mors.Journeys.Data.Queries; 9 | using Mors.Journeys.Data.Queries.Dtos; 10 | 11 | namespace Mors.Journeys.Application.Client.Wpf.Features.CalculatePassengerLiftsCostInPeriod 12 | { 13 | internal sealed class CalculatePassengerLiftsCostInPeriodViewModel : INotifyPropertyChanged 14 | { 15 | private readonly IQueryDispatcher _queryDispatcher; 16 | private decimal _totalCost; 17 | private List _passengers; 18 | 19 | public CalculatePassengerLiftsCostInPeriodViewModel(IQueryDispatcher queryDispatcher) 20 | { 21 | _queryDispatcher = queryDispatcher; 22 | CalculateCommand = new DelegateCommand(Calculate); 23 | } 24 | 25 | private void Calculate() 26 | { 27 | try 28 | { 29 | TotalCost = _queryDispatcher.Dispatch(new GetCostOfPassengerLiftsInPeriodQuery(Passenger, new Period(From, To))).TotalCost; 30 | } 31 | catch 32 | { 33 | TotalCost = 0m; 34 | } 35 | } 36 | 37 | public void Refresh() 38 | { 39 | try 40 | { 41 | Passengers = _queryDispatcher.Dispatch(new GetPeopleNamesQuery()).ToList(); 42 | } 43 | catch 44 | { 45 | Passengers = new List(); 46 | } 47 | } 48 | 49 | public object Passenger { get; set; } 50 | 51 | public DateTime From { get; set; } 52 | 53 | public DateTime To { get; set; } 54 | 55 | public List Passengers 56 | { 57 | get { return _passengers; } 58 | set 59 | { 60 | _passengers = value; 61 | PropertyChanged.Raise(this); 62 | } 63 | } 64 | 65 | public decimal TotalCost 66 | { 67 | get { return _totalCost; } 68 | set 69 | { 70 | _totalCost = value; 71 | PropertyChanged.Raise(this); 72 | } 73 | } 74 | 75 | public ICommand CalculateCommand { get; private set; } 76 | 77 | public event PropertyChangedEventHandler PropertyChanged; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/CalendarContentProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mors.Journeys.Data.Queries; 5 | using Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 8 | { 9 | internal sealed class CalendarContentProvider 10 | { 11 | private readonly IQueryDispatcher _queryDispatcher; 12 | private readonly Dictionary _contents; 13 | 14 | public CalendarContentProvider(IQueryDispatcher queryDispatcher) 15 | { 16 | _queryDispatcher = queryDispatcher; 17 | _contents = new Dictionary(); 18 | } 19 | 20 | public void Refresh() 21 | { 22 | var facts = _queryDispatcher.Dispatch(new GetJourneysByPassengerThenMonthThenDayQuery()); 23 | foreach (var fact in facts) 24 | { 25 | JourneyDaySummary content; 26 | if (_contents.TryGetValue(fact.Key, out content)) 27 | { 28 | content.Change(fact.Value); 29 | } 30 | else 31 | { 32 | _contents[fact.Key] = new JourneyDaySummary(fact.Value); 33 | } 34 | } 35 | } 36 | 37 | public Func GetContentProviderForDay(Passenger passenger, Month month) 38 | { 39 | return dayOfMonth => _contents 40 | .Where(fact => 41 | fact.Key.Month.Year == month.Year && 42 | fact.Key.Month.MonthOfYear == month.MonthInYear && 43 | fact.Key.Passenger.Id.Equals(passenger.Id) && 44 | fact.Key.Day.DayOfMonth == dayOfMonth) 45 | .Select(fact => fact.Value) 46 | .FirstOrDefault(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/JourneyCalendarsControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 4 | { 5 | internal sealed class JourneyCalendarsControl : Control 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/JourneyCalendarsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using Mors.Journeys.Application.Client.Wpf.Events; 5 | using Mors.Journeys.Data.Queries; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 8 | { 9 | internal sealed class JourneyCalendarsViewModel 10 | { 11 | private readonly IQueryDispatcher _queryDispatcher; 12 | private readonly IEventBus _eventBus; 13 | private readonly CalendarContentProvider _calendarContentProvider; 14 | private readonly ObservableCollection _calendars; 15 | private readonly ReadOnlyObservableCollection _calendarsWrapper; 16 | 17 | public JourneyCalendarsViewModel(IQueryDispatcher queryDispatcher, IEventBus eventBus) 18 | { 19 | _queryDispatcher = queryDispatcher; 20 | _eventBus = eventBus; 21 | _calendarContentProvider = new CalendarContentProvider(queryDispatcher); 22 | _calendars = new ObservableCollection(); 23 | _calendarsWrapper = new ReadOnlyObservableCollection(_calendars); 24 | _eventBus.RegisterListener(Handle); 25 | } 26 | 27 | private void Handle(JourneyWithLiftsAddedEvent @event) 28 | { 29 | _calendarContentProvider.Refresh(); 30 | foreach (var calendar in _calendars) 31 | { 32 | calendar.Refresh(); 33 | } 34 | } 35 | 36 | public ReadOnlyObservableCollection Calendars { get { return _calendarsWrapper; } } 37 | 38 | public void Refresh() 39 | { 40 | var peopleNames = _queryDispatcher.Dispatch(new GetPeopleNamesQuery()); 41 | var currentMonth = new Month(DateTime.Now.Year, DateTime.Now.Month); 42 | _calendars.Clear(); 43 | _calendarContentProvider.Refresh(); 44 | foreach (var calendar in peopleNames.Select(personName => new PassengerLiftCalendar(new Passenger(personName), currentMonth, _calendarContentProvider))) 45 | { 46 | _calendars.Add(calendar); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/JourneyDaySummary.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 3 | using Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 6 | { 7 | internal sealed class JourneyDaySummary : INotifyPropertyChanged 8 | { 9 | private Value _value; 10 | 11 | public JourneyDaySummary(Value value) 12 | { 13 | _value = value; 14 | } 15 | 16 | public void Change(Value value) 17 | { 18 | _value = value; 19 | PropertyChanged.Raise(this, () => LiftSummary); 20 | PropertyChanged.Raise(this, () => JourneySummary); 21 | } 22 | 23 | public string LiftSummary { get { return string.Format("{0} / {1}", _value.LiftCount, _value.LiftDistance); } } 24 | 25 | public string JourneySummary { get { return string.Format("{0} / {1}", _value.JourneyCount, _value.JourneyDistance); } } 26 | 27 | public event PropertyChangedEventHandler PropertyChanged = delegate { }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/Month.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 4 | { 5 | internal sealed class Month 6 | { 7 | public Month(int year, int monthInYear) 8 | { 9 | Year = year; 10 | MonthInYear = monthInYear; 11 | } 12 | 13 | public int Year { get; private set; } 14 | 15 | public int MonthInYear { get; private set; } 16 | 17 | public string MonthName 18 | { 19 | get { return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(MonthInYear); } 20 | } 21 | 22 | public Month Next() 23 | { 24 | return MonthInYear >= 12 25 | ? new Month(Year + 1, 1) 26 | : new Month(Year, MonthInYear + 1); 27 | } 28 | 29 | public Month Previous() 30 | { 31 | return MonthInYear <= 1 32 | ? new Month(Year - 1, 12) 33 | : new Month(Year, MonthInYear - 1); 34 | } 35 | 36 | public override bool Equals(object obj) 37 | { 38 | return obj is Month 39 | && Equals((Month)obj); 40 | } 41 | 42 | public bool Equals(Month other) 43 | { 44 | return other != null 45 | && other.MonthInYear == MonthInYear 46 | && other.Year == Year; 47 | } 48 | 49 | public override int GetHashCode() 50 | { 51 | return (MonthInYear.GetHashCode() * 37) ^ Year.GetHashCode(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/MonthSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Input; 4 | using Mors.Journeys.Application.Client.Wpf.Infrastructure; 5 | using Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 8 | { 9 | internal sealed class MonthSelector : INotifyPropertyChanged 10 | { 11 | private Month _current; 12 | 13 | public MonthSelector(Month current) 14 | { 15 | _current = current; 16 | } 17 | 18 | public void Select(Month month) 19 | { 20 | Current = month; 21 | } 22 | 23 | public void Next() 24 | { 25 | Current = Current.Next(); 26 | } 27 | 28 | public void Previous() 29 | { 30 | Current = Current.Previous(); 31 | } 32 | 33 | public ICommand NextCommand 34 | { 35 | get { return new DelegateCommand(Next); } 36 | } 37 | 38 | public ICommand PreviousCommand 39 | { 40 | get { return new DelegateCommand(Previous); } 41 | } 42 | 43 | public Month Current 44 | { 45 | get { return _current; } 46 | private set 47 | { 48 | _current = value; 49 | PropertyChanged.Raise(this); 50 | CurrentChanged.Raise(this); 51 | } 52 | } 53 | 54 | public event PropertyChangedEventHandler PropertyChanged; 55 | 56 | public event EventHandler CurrentChanged; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/Passenger.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Data.Queries.Dtos; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 4 | { 5 | internal sealed class Passenger 6 | { 7 | private readonly PersonName _passengerName; 8 | 9 | public Passenger(PersonName passengerName) 10 | { 11 | _passengerName = passengerName; 12 | } 13 | 14 | public string Name { get { return _passengerName.Name; } } 15 | 16 | public object Id { get { return _passengerName.OwnerId; } } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Features/ShowJourneysInCalendar/PassengerLiftCalendar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Application.Client.Wpf.Components.Calendar; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar 5 | { 6 | internal sealed class PassengerLiftCalendar 7 | { 8 | private readonly CalendarContentProvider _contentProvider; 9 | private readonly Passenger _passenger; 10 | private readonly MonthSelector _monthSelector; 11 | private readonly CalendarMonth _monthCalendar; 12 | 13 | public PassengerLiftCalendar(Passenger passenger, Month initialMonth, CalendarContentProvider contentProvider) 14 | { 15 | _contentProvider = contentProvider; 16 | _passenger = passenger; 17 | _monthSelector = new MonthSelector(initialMonth); 18 | _monthCalendar = new CalendarMonth(); 19 | _monthSelector.CurrentChanged += OnCurrentMonthChanged; 20 | ChangeThenFill(); 21 | } 22 | 23 | public MonthSelector MonthSelector { get { return _monthSelector; } } 24 | 25 | public CalendarMonth MonthCalendar { get { return _monthCalendar; } } 26 | 27 | public string PassengerName { get { return _passenger.Name; } } 28 | 29 | private void OnCurrentMonthChanged(object sender, EventArgs e) 30 | { 31 | ChangeThenFill(); 32 | } 33 | 34 | public void Refresh() 35 | { 36 | Fill(); 37 | } 38 | 39 | private void ChangeThenFill() 40 | { 41 | var currentMonth = _monthSelector.Current; 42 | _monthCalendar.Change(currentMonth.Year, currentMonth.MonthInYear); 43 | Fill(); 44 | } 45 | 46 | private void Fill() 47 | { 48 | var dayContentProvider = _contentProvider.GetContentProviderForDay(_passenger, MonthSelector.Current); 49 | _monthCalendar.Fill(dayContentProvider); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/ICommandDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.Client.Wpf 2 | { 3 | public interface ICommandDispatcher 4 | { 5 | void Dispatch(TCommand command); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/ICommandHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf 4 | { 5 | public interface ICommandHandlerRegistry 6 | { 7 | void SetHandler(Action handler); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/IEventBus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf 4 | { 5 | public interface IEventBus 6 | { 7 | void Publish(TEvent @event); 8 | 9 | void RegisterListener(Action handler); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/IIdFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.Client.Wpf 2 | { 3 | public interface IIdFactory 4 | { 5 | object Create(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/IQueryDispatcher.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Data; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf 4 | { 5 | public interface IQueryDispatcher 6 | { 7 | TResult Dispatch(IQuery query); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/IQueryHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Data; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf 5 | { 6 | public interface IQueryHandlerRegistry 7 | { 8 | void SetHandler(Func handler) 9 | where TQuery : IQuery; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure 5 | { 6 | internal sealed class DelegateCommand : ICommand 7 | { 8 | private readonly Action _handler; 9 | 10 | public DelegateCommand(Action handler) 11 | { 12 | if (handler == null) throw new ArgumentNullException("handler"); 13 | _handler = handler; 14 | } 15 | 16 | public bool CanExecute(object parameter) 17 | { 18 | return true; 19 | } 20 | 21 | public event EventHandler CanExecuteChanged { add {} remove {} } 22 | 23 | public void Execute(object parameter) 24 | { 25 | _handler(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/DelegateCommand`1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure 5 | { 6 | internal sealed class DelegateCommand : ICommand 7 | { 8 | private readonly Action _handler; 9 | 10 | public DelegateCommand(Action handler) 11 | { 12 | if (handler == null) throw new ArgumentNullException("handler"); 13 | _handler = handler; 14 | } 15 | 16 | public bool CanExecute(object parameter) 17 | { 18 | return true; 19 | } 20 | 21 | public event EventHandler CanExecuteChanged { add { } remove { } } 22 | 23 | public void Execute(object parameter) 24 | { 25 | if (parameter is TParameter) 26 | { 27 | _handler((TParameter)parameter); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/Extensions/EventExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions 4 | { 5 | internal static class EventExtensions 6 | { 7 | public static void Raise(this EventHandler handler, object sender) 8 | { 9 | var copy = handler; 10 | if (copy != null) 11 | { 12 | copy(sender, EventArgs.Empty); 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/Extensions/NotifyCollectionChangedEventExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions 4 | { 5 | internal static class NotifyCollectionChangedEventExtensions 6 | { 7 | public static void Raise(this NotifyCollectionChangedEventHandler handler, object sender, NotifyCollectionChangedEventArgs args) 8 | { 9 | if (handler == null || args == null) return; 10 | handler(sender, args); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/Extensions/PropertyChangedEventExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq.Expressions; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure.Extensions 7 | { 8 | internal static class PropertyChangedEventExtensions 9 | { 10 | public static void Raise(this PropertyChangedEventHandler handler, object sender, [CallerMemberName] string propertyName = null) 11 | { 12 | if (handler == null || propertyName == null) return; 13 | handler(sender, new PropertyChangedEventArgs(propertyName)); 14 | } 15 | 16 | public static void Raise(this PropertyChangedEventHandler handler, object sender, Expression> propertyExpression) 17 | { 18 | if (handler == null || propertyExpression == null) return; 19 | var propertyBodyExpression = propertyExpression.Body; 20 | var propertyAccessExpression = propertyBodyExpression.NodeType == ExpressionType.MemberAccess 21 | ? (MemberExpression)propertyBodyExpression 22 | : (MemberExpression)((UnaryExpression)propertyBodyExpression).Operand; 23 | handler.Raise(sender, propertyAccessExpression.Member.Name); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Infrastructure/Interfaces/INotifyCollectionChangedReadOnlyList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf.Infrastructure.Interfaces 5 | { 6 | internal interface INotifyCollectionChangedReadOnlyList : IReadOnlyList, INotifyCollectionChanged 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/MainPanel.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/MainPanel.xaml.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Application.Client.Wpf.Features.AddJourneysWithLifts; 2 | using Mors.Journeys.Application.Client.Wpf.Features.CalculatePassengerLiftsCostInPeriod; 3 | using Mors.Journeys.Application.Client.Wpf.Features.ShowJourneysInCalendar; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf 6 | { 7 | internal partial class MainPanel 8 | { 9 | public MainPanel( 10 | ICommandDispatcher commandDispatcher, 11 | IQueryDispatcher queryDispatcher, 12 | IEventBus eventBus, 13 | IIdFactory idFactory) 14 | { 15 | InitializeComponent(); 16 | AddJourney.DataContext = new AddJourneyWithLiftsViewModel(commandDispatcher, queryDispatcher, eventBus, idFactory); 17 | var journeyCalendarsViewModel = new JourneyCalendarsViewModel(queryDispatcher, eventBus); 18 | journeyCalendarsViewModel.Refresh(); 19 | JourneyCalendars.DataContext = journeyCalendarsViewModel; 20 | var calculatePassengerLiftsCostInPeriod = new CalculatePassengerLiftsCostInPeriodViewModel(queryDispatcher); 21 | calculatePassengerLiftsCostInPeriod.Refresh(); 22 | CalculatePassengerLiftsCostInPeriod.DataContext = calculatePassengerLiftsCostInPeriod; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Mors.Journeys.Application.Client.Wpf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | Copyright © Łukasz Mrozek 2019 6 | https://github.com/morsiu/Journeys 7 | 3.0.0 8 | Desktop client for Journeys application 9 | Łukasz Mrozek 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | SettingsSingleFileGenerator 28 | 29 | 30 | 31 | 32 | 33 | Designer 34 | MSBuild:UpdateDesignTimeXaml 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Application.Client.Wpf.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mors.Journeys.Application.Client.Wpf.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Application.Client.Wpf.Settings { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | public global::Mors.Journeys.Application.Client.Wpf.Settings.JourneyTemplateCollection JourneyTemplates { 29 | get { 30 | return ((global::Mors.Journeys.Application.Client.Wpf.Settings.JourneyTemplateCollection)(this["JourneyTemplates"])); 31 | } 32 | set { 33 | this["JourneyTemplates"] = value; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Queries/GetJourneyTemplatesQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Mors.Journeys.Application.Client.Wpf.Settings; 3 | using Mors.Journeys.Data; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Queries 6 | { 7 | internal sealed class GetJourneyTemplatesQuery : IQuery> 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Resources.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Settings/JourneyTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Mors.Journeys.Application.Client.Wpf.Settings 6 | { 7 | [Serializable] 8 | public sealed class JourneyTemplate 9 | { 10 | public string Name { get; set; } 11 | 12 | public decimal RouteDistance { get; set; } 13 | 14 | public List Lifts { get; set; } 15 | 16 | public JourneyTemplate Clone() 17 | { 18 | return new JourneyTemplate 19 | { 20 | Name = Name, 21 | RouteDistance = RouteDistance, 22 | Lifts = Lifts.Select(t => t.Clone()).ToList() 23 | }; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Settings/JourneyTemplateCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mors.Journeys.Application.Client.Wpf.Settings 5 | { 6 | [Serializable] 7 | public sealed class JourneyTemplateCollection 8 | { 9 | public List Templates { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Settings/LiftTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application.Client.Wpf.Settings 4 | { 5 | [Serializable] 6 | public sealed class LiftTemplate 7 | { 8 | public string PassengerName { get; set; } 9 | 10 | public decimal LiftDistance { get; set; } 11 | 12 | public LiftTemplate Clone() 13 | { 14 | return new LiftTemplate 15 | { 16 | PassengerName = PassengerName, 17 | LiftDistance = LiftDistance 18 | }; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/Settings/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mors.Journeys.Application.Client.Wpf.Commands; 5 | using Mors.Journeys.Application.Client.Wpf.Queries; 6 | 7 | namespace Mors.Journeys.Application.Client.Wpf.Settings 8 | { 9 | internal sealed partial class Settings 10 | { 11 | public List Execute(GetJourneyTemplatesQuery query) 12 | { 13 | return JourneyTemplates == null 14 | ? new List() 15 | : JourneyTemplates.Templates.Select(t => t.Clone()).ToList(); 16 | } 17 | 18 | internal void Handle(StoreJourneyTemplatesCommand command) 19 | { 20 | if (JourneyTemplates == null) JourneyTemplates = new JourneyTemplateCollection { Templates = new List() }; 21 | var templates = JourneyTemplates.Templates; 22 | foreach (var newTemplate in command.AddedTemplates) 23 | { 24 | if (string.IsNullOrWhiteSpace(newTemplate.Name)) throw new InvalidOperationException("Journey template name cannot be empty."); 25 | var existingTemplate = templates.FirstOrDefault(t => t.Name == newTemplate.Name); 26 | if (existingTemplate != null) 27 | { 28 | templates.Remove(existingTemplate); 29 | } 30 | templates.Add(newTemplate.Clone()); 31 | } 32 | foreach (var removedTemplateName in command.RemovedTemplateNames) 33 | { 34 | var removedTemplate = templates.FirstOrDefault(t => t.Name == removedTemplateName); 35 | if (removedTemplate != null) 36 | { 37 | templates.Remove(removedTemplate); 38 | } 39 | } 40 | Save(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Mors.Journeys.Application.Client.Wpf/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/Bootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Mors.Journeys.Application.CommandHandlers; 4 | using Mors.Journeys.Application.EventReplayers; 5 | using Mors.Journeys.Application.QueryHandlers; 6 | using Mors.Journeys.Data.Commands; 7 | using Mors.Journeys.Data.Events; 8 | using Mors.Journeys.Data.Queries; 9 | using Mors.Journeys.Data.Queries.Dtos; 10 | using Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay; 11 | 12 | namespace Mors.Journeys.Application 13 | { 14 | public sealed class Bootstrapper 15 | { 16 | public void BootstrapCommands( 17 | ICommandHandlerRegistry commandHandlerRegistry, 18 | IRepositories repositories, 19 | Action eventPublisher, 20 | Func idFactory, 21 | IQueryDispatcher queryDispatcher) 22 | { 23 | commandHandlerRegistry.SetHandler( 24 | new AddJourneyWithLiftsCommandHandler(eventPublisher, repositories, idFactory, queryDispatcher).Execute); 25 | } 26 | 27 | public void BootstrapEventSourcing( 28 | IEventSourcing eventSourcing, 29 | IRepositories repositories, 30 | Action eventPublisher) 31 | { 32 | eventSourcing.RegisterEventReplayer(new JourneyCreatedEventReplayer(repositories, eventPublisher).Replay); 33 | eventSourcing.RegisterEventReplayer(new LiftAddedEventReplayer(repositories).Replay); 34 | eventSourcing.RegisterEventReplayer(new PersonCreatedEventReplayer(repositories, eventPublisher).Replay); 35 | } 36 | 37 | public void BootstrapQueries( 38 | IQueryHandlerRegistry queryHandlerRegistry, 39 | IEventBus eventBus, 40 | IQueryDispatcher queryDispatcher) 41 | { 42 | var personView = new PersonView(); 43 | queryHandlerRegistry.SetHandler(personView.Execute); 44 | queryHandlerRegistry.SetHandler>(personView.Execute); 45 | eventBus.RegisterListener(personView.Update); 46 | 47 | var journeysByPassengerThenMonthThenDayView = new JourneysByPassengerThenMonthThenDayView(); 48 | queryHandlerRegistry.SetHandler>(journeysByPassengerThenMonthThenDayView.Execute); 49 | eventBus.RegisterListener(journeysByPassengerThenMonthThenDayView.Update); 50 | eventBus.RegisterListener(journeysByPassengerThenMonthThenDayView.Update); 51 | 52 | var journeyView = new JourneyView(); 53 | queryHandlerRegistry.SetHandler>(journeyView.Execute); 54 | eventBus.RegisterListener(journeyView.Update); 55 | eventBus.RegisterListener(journeyView.Update); 56 | 57 | var passengerLiftsCostCalculator = new PassengerLiftCostCalculator(queryDispatcher); 58 | queryHandlerRegistry.SetHandler(passengerLiftsCostCalculator.Execute); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/CommandHandlers/AddJourneyWithLiftsCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Application; 3 | using Mors.Journeys.Data.Commands; 4 | using Mors.Journeys.Data.Queries; 5 | using Mors.Journeys.Domain.Journeys.Capabilities; 6 | using Mors.Journeys.Domain.Journeys.Operations; 7 | using Mors.Journeys.Domain.People; 8 | 9 | namespace Mors.Journeys.Application.CommandHandlers 10 | { 11 | internal sealed class AddJourneyWithLiftsCommandHandler 12 | { 13 | private readonly Action _eventPublisher; 14 | private readonly IQueryDispatcher _queryDispatcher; 15 | private readonly IRepositories _repositories; 16 | private readonly Func _idFactory; 17 | 18 | public AddJourneyWithLiftsCommandHandler( 19 | Action eventPublisher, 20 | IRepositories repositories, 21 | Func idFactory, 22 | IQueryDispatcher queryDispatcher) 23 | { 24 | _idFactory = idFactory; 25 | _queryDispatcher = queryDispatcher; 26 | _repositories = repositories; 27 | _eventPublisher = eventPublisher; 28 | } 29 | 30 | public void Execute(AddJourneyWithLiftsCommand command) 31 | { 32 | var routeDistance = new Distance(command.RouteDistance, DistanceUnit.Kilometer); 33 | var journey = new Journey(command.JourneyId, command.DateOfOccurrence, routeDistance, _eventPublisher); 34 | foreach (var lift in command.Lifts) 35 | { 36 | var liftDistance = new Distance(lift.LiftDistance, DistanceUnit.Kilometer); 37 | var personId = GetOrAddPersonWithName(lift.PersonName); 38 | journey = journey.AddLift(personId, liftDistance); 39 | } 40 | _repositories.Store(journey); 41 | } 42 | 43 | private object GetOrAddPersonWithName(string personName) 44 | { 45 | var personId = _queryDispatcher.Dispatch(new GetPersonIdByNameQuery(personName)); 46 | if (personId == null) 47 | { 48 | personId = _idFactory(); 49 | var person = new Person(personId, personName, _eventPublisher); 50 | _repositories.Store(person); 51 | } 52 | return personId; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/EventReplayers/JourneyCreatedEventReplayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Application; 3 | using Mors.Journeys.Data.Events; 4 | using Mors.Journeys.Domain.Journeys.Capabilities; 5 | using Mors.Journeys.Domain.Journeys.Operations; 6 | 7 | namespace Mors.Journeys.Application.EventReplayers 8 | { 9 | internal sealed class JourneyCreatedEventReplayer 10 | { 11 | private readonly IRepositories _journeyRepository; 12 | private readonly Action _eventPublisher; 13 | 14 | public JourneyCreatedEventReplayer(IRepositories journeyRepository, Action eventPublisher) 15 | { 16 | _journeyRepository = journeyRepository; 17 | _eventPublisher = eventPublisher; 18 | } 19 | 20 | public void Replay(JourneyCreatedEvent @event) 21 | { 22 | var routeDistance = new Distance(@event.RouteDistance, DistanceUnit.Kilometer); 23 | var journey = new Journey(@event.JourneyId, @event.DateOfOccurrence, routeDistance, _eventPublisher); 24 | _journeyRepository.Store(journey); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/EventReplayers/LiftAddedEventReplayer.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Application; 2 | using Mors.Journeys.Data.Events; 3 | using Mors.Journeys.Domain.Journeys.Capabilities; 4 | using Mors.Journeys.Domain.Journeys.Operations; 5 | 6 | namespace Mors.Journeys.Application.EventReplayers 7 | { 8 | internal sealed class LiftAddedEventReplayer 9 | { 10 | private readonly IRepositories _repositories; 11 | 12 | public LiftAddedEventReplayer(IRepositories repositories) 13 | { 14 | _repositories = repositories; 15 | } 16 | 17 | public void Replay(LiftAddedEvent @event) 18 | { 19 | var liftDistance = new Distance(@event.LiftDistance, DistanceUnit.Kilometer); 20 | var journey = _repositories 21 | .Get(@event.JourneyId) 22 | .AddLift(@event.PersonId, liftDistance); 23 | _repositories.Store(journey); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/EventReplayers/PersonCreatedEventReplayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Application; 3 | using Mors.Journeys.Data.Events; 4 | using Mors.Journeys.Domain.People; 5 | 6 | namespace Mors.Journeys.Application.EventReplayers 7 | { 8 | internal sealed class PersonCreatedEventReplayer 9 | { 10 | private readonly IRepositories _repositories; 11 | private readonly Action _eventPublisher; 12 | 13 | public PersonCreatedEventReplayer(IRepositories repositories, Action eventPublisher) 14 | { 15 | _repositories = repositories; 16 | _eventPublisher = eventPublisher; 17 | } 18 | 19 | public void Replay(PersonCreatedEvent @event) 20 | { 21 | var personId = @event.PersonId; 22 | var person = new Person(personId, @event.PersonName, _eventPublisher); 23 | _repositories.Store(person); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/ICommandHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application 4 | { 5 | public interface ICommandHandlerRegistry 6 | { 7 | void SetHandler(Action handler); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/IEventBus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application 4 | { 5 | public interface IEventBus 6 | { 7 | void RegisterListener(Action handler); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/IEventSourcing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application 4 | { 5 | public interface IEventSourcing 6 | { 7 | void RegisterEventReplayer(Action eventReplayer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/IQueryDispatcher.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Data; 2 | 3 | namespace Mors.Journeys.Application 4 | { 5 | public interface IQueryDispatcher 6 | { 7 | TResult Dispatch(IQuery query); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/IQueryHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Data; 3 | 4 | namespace Mors.Journeys.Application 5 | { 6 | public interface IQueryHandlerRegistry 7 | { 8 | void SetHandler(Func handler) 9 | where TQuery : IQuery; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/IRepositories.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Data; 2 | 3 | namespace Mors.Journeys.Application 4 | { 5 | public interface IRepositories 6 | { 7 | TEntity Get(object id) where TEntity : IHasId; 8 | 9 | void Store(TEntity entity) where TEntity : IHasId; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/Mors.Journeys.Application.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard20 5 | 3.0.0 6 | Copyright © Łukasz Mrozek 2019 7 | Łukasz Mrozek 8 | Journeys application 9 | https://github.com/morsiu/Journeys 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Period.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure 4 | { 5 | internal sealed class Period 6 | { 7 | private readonly DateTime _end; 8 | private readonly DateTime _start; 9 | private readonly bool _isEmpty; 10 | 11 | public Period(DateTime start, DateTime end) 12 | { 13 | if (start <= end) 14 | { 15 | _start = start; 16 | _end = end; 17 | } 18 | else 19 | { 20 | _isEmpty = true; 21 | } 22 | } 23 | 24 | public bool Contains(DateTime point) 25 | { 26 | if (_isEmpty) return false; 27 | return _start <= point && _end >= point; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/IMaybe.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 2 | { 3 | internal interface IMaybe 4 | { 5 | T Value { get; } 6 | 7 | bool HasValue { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/Just.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 2 | { 3 | internal sealed class Just : IMaybe 4 | { 5 | public Just(T value) 6 | { 7 | Value = value; 8 | } 9 | 10 | public T Value { get; private set; } 11 | 12 | public bool HasValue 13 | { 14 | get { return true; } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/Nothing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Application.QueryHandlers.Messages; 3 | 4 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 5 | { 6 | internal sealed class Nothing : IMaybe 7 | { 8 | public T Value 9 | { 10 | get { throw new InvalidOperationException(FailureMessages.NothingObjectHasNoValue); } 11 | } 12 | 13 | public bool HasValue 14 | { 15 | get { return false; } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 2 | { 3 | internal static class ObjectExtensions 4 | { 5 | public static bool IsNotNull(this T value) 6 | { 7 | return !ReferenceEquals(value, null); 8 | } 9 | 10 | public static bool IsNull(this T value) 11 | { 12 | return ReferenceEquals(value, null); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/ValueLookup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 5 | { 6 | /// 7 | /// Stores values accessible by keys. 8 | /// The keys may be unrelated to values. 9 | /// 10 | internal sealed class ValueLookup 11 | { 12 | private readonly Dictionary _values = new Dictionary(); 13 | 14 | public void Set(TKey key, TValue value) 15 | { 16 | if (key.IsNull()) throw new ArgumentNullException("key"); 17 | 18 | _values[key] = value; 19 | } 20 | 21 | public IMaybe Get(TKey key) 22 | { 23 | if (key.IsNull()) throw new ArgumentNullException("key"); 24 | 25 | TValue value; 26 | if (_values.TryGetValue(key, out value)) 27 | { 28 | return new Just(value); 29 | } 30 | return new Nothing(); 31 | } 32 | 33 | public TValue GetOrAdd(TKey key, Func newValue) 34 | { 35 | if (key.IsNull()) throw new ArgumentNullException("key"); 36 | if (newValue.IsNull()) throw new ArgumentNullException("newValue"); 37 | 38 | var result = Get(key); 39 | if (result.HasValue) 40 | { 41 | return result.Value; 42 | } 43 | var value = newValue(); 44 | Set(key, value); 45 | return value; 46 | } 47 | 48 | public IEnumerable> Retrieve() 49 | { 50 | return _values; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/ValueMultiSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Mors.Journeys.Application.QueryHandlers.Messages; 4 | 5 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 6 | { 7 | internal sealed class ValueMultiSet 8 | { 9 | private static readonly IReadOnlyCollection _emptyList = new List(); 10 | private readonly Func _keyGenerator; 11 | private readonly Dictionary> _values; 12 | 13 | public ValueMultiSet(Func keyGenerator) 14 | { 15 | if (keyGenerator.IsNull()) throw new ArgumentNullException("keyGenerator"); 16 | 17 | _keyGenerator = keyGenerator; 18 | _values = new Dictionary>(); 19 | } 20 | 21 | public void Add(TValue value) 22 | { 23 | var key = _keyGenerator(value); 24 | if (key.IsNull()) throw new ArgumentException(FailureMessages.CannotAddValueWithNullKey, "value"); 25 | var list = AddOrGetList(key); 26 | list.Add(value); 27 | } 28 | 29 | private ICollection AddOrGetList(TKey key) 30 | { 31 | List list; 32 | if (_values.TryGetValue(key, out list)) 33 | { 34 | return list; 35 | } 36 | var newList = new List(); 37 | _values[key] = newList; 38 | return newList; 39 | } 40 | 41 | public IReadOnlyCollection Get(TKey key) 42 | { 43 | return _values[key]; 44 | } 45 | 46 | public IReadOnlyCollection GetOrDefault(TKey key) 47 | { 48 | return HasValueForKey(key) ? _values[key] : _emptyList; 49 | } 50 | 51 | private bool HasValueForKey(TKey key) 52 | { 53 | return key.IsNotNull() && _values.ContainsKey(key); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Infrastructure/Views/ValueSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Mors.Journeys.Application.QueryHandlers.Messages; 4 | 5 | namespace Mors.Journeys.Application.QueryHandlers.Infrastructure.Views 6 | { 7 | /// 8 | /// Stores values that are accessible by keys. 9 | /// The keys are computed from values. 10 | /// 11 | internal sealed class ValueSet 12 | { 13 | private readonly Func _keyGenerator; 14 | private readonly Dictionary _values; 15 | 16 | public ValueSet(Func keyGenerator) 17 | { 18 | if (keyGenerator.IsNull()) throw new ArgumentNullException("keyGenerator"); 19 | 20 | _keyGenerator = keyGenerator; 21 | _values = new Dictionary(); 22 | } 23 | 24 | public void Add(TValue value) 25 | { 26 | var key = _keyGenerator(value); 27 | if (key.IsNull()) throw new ArgumentException(FailureMessages.CannotAddValueWithNullKey, "value"); 28 | _values.Add(key, value); 29 | } 30 | 31 | public TValue Get(TKey key) 32 | { 33 | return _values[key]; 34 | } 35 | 36 | public TValue Get(TKey key, Func defaultValue) 37 | { 38 | return HasValueForKey(key) ? _values[key] : defaultValue(); 39 | } 40 | 41 | public IEnumerable Retrieve() 42 | { 43 | return _values.Values; 44 | } 45 | 46 | private bool HasValueForKey(TKey key) 47 | { 48 | return key.IsNotNull() && _values.ContainsKey(key); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/JourneyView.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mors.Journeys.Application.QueryHandlers.Infrastructure.Views; 4 | using Mors.Journeys.Data.Events; 5 | using Mors.Journeys.Data.Queries; 6 | using Mors.Journeys.Data.Queries.Dtos; 7 | using Period = Mors.Journeys.Application.QueryHandlers.Infrastructure.Period; 8 | 9 | namespace Mors.Journeys.Application.QueryHandlers 10 | { 11 | internal sealed class JourneyView 12 | { 13 | private readonly ValueSet _journeys; 14 | private readonly ValueMultiSet _lifts; 15 | 16 | public JourneyView() 17 | { 18 | _journeys = new ValueSet(evt => evt.JourneyId); 19 | _lifts = new ValueMultiSet(evt => evt.JourneyId); 20 | } 21 | 22 | public void Update(JourneyCreatedEvent @event) 23 | { 24 | _journeys.Add(@event); 25 | } 26 | 27 | public void Update(LiftAddedEvent @event) 28 | { 29 | _lifts.Add(@event); 30 | } 31 | 32 | public IEnumerable Execute(GetJourneysInPeriodQuery query) 33 | { 34 | var period = new Period(query.Period.Start, query.Period.End); 35 | var journeysInPeriod = _journeys.Retrieve().Where(j => period.Contains(j.DateOfOccurrence)); 36 | return journeysInPeriod.Select(ToDto).ToList(); 37 | } 38 | 39 | private Journey ToDto(JourneyCreatedEvent journey) 40 | { 41 | var lifts = _lifts.GetOrDefault(journey.JourneyId); 42 | return new Journey( 43 | journey.JourneyId, 44 | journey.DateOfOccurrence, 45 | journey.RouteDistance, 46 | lifts.Select(ToDto).ToList()); 47 | } 48 | 49 | private static Lift ToDto(LiftAddedEvent lift) 50 | { 51 | return new Lift( 52 | lift.PersonId, 53 | lift.LiftDistance); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/JourneysByPassengerThenMonthThenDayView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mors.Journeys.Application.QueryHandlers.Infrastructure.Views; 5 | using Mors.Journeys.Data.Events; 6 | using Mors.Journeys.Data.Queries; 7 | using Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay; 8 | 9 | namespace Mors.Journeys.Application.QueryHandlers 10 | { 11 | internal sealed class JourneysByPassengerThenMonthThenDayView 12 | { 13 | private readonly ValueLookup _facts = new ValueLookup(); 14 | private readonly ValueSet _journeys = new ValueSet(journey => journey.JourneyId); 15 | private readonly ValueLookup> _journeysByDate = new ValueLookup>(); 16 | 17 | public IEnumerable Execute(GetJourneysByPassengerThenMonthThenDayQuery query) 18 | { 19 | return _facts.Retrieve().Select(factEntry => new Fact(factEntry.Key, factEntry.Value)).ToList(); 20 | } 21 | 22 | public void Update(JourneyCreatedEvent @event) 23 | { 24 | _journeys.Add(@event); 25 | var journeysOnDateOfOccurence = _journeysByDate.GetOrAdd(@event.DateOfOccurrence, () => new HashSet()); 26 | journeysOnDateOfOccurence.Add(@event); 27 | var dateOfOccurrence = @event.DateOfOccurrence; 28 | foreach (var factEntry in _facts.Retrieve().Where(f => f.Key.Day.DayOfMonth == dateOfOccurrence.Day && f.Key.Month.MonthOfYear == dateOfOccurrence.Month && f.Key.Month.Year == dateOfOccurrence.Year).ToList()) 29 | { 30 | _facts.Set(factEntry.Key, UpdateValue(factEntry.Value, @event)); 31 | } 32 | } 33 | 34 | public void Update(LiftAddedEvent @event) 35 | { 36 | var journey = _journeys.Get(@event.JourneyId); 37 | var dateOfOccurrence = journey.DateOfOccurrence; 38 | var key = new Key(new Passenger(@event.PersonId), new Month(dateOfOccurrence.Year, dateOfOccurrence.Month), new Day(dateOfOccurrence.Day)); 39 | var value = _facts.GetOrAdd(key, () => CreateValue(dateOfOccurrence)); 40 | _facts.Set(key, UpdateValue(value, @event)); 41 | } 42 | 43 | private static Value UpdateValue(Value value, JourneyCreatedEvent @event) 44 | { 45 | return new Value( 46 | value.JourneyCount + 1, 47 | value.JourneyDistance + @event.RouteDistance, 48 | value.LiftCount, 49 | value.LiftDistance); 50 | } 51 | 52 | private static Value UpdateValue(Value value, LiftAddedEvent @event) 53 | { 54 | return new Value( 55 | value.JourneyCount, 56 | value.JourneyDistance, 57 | value.LiftCount + 1, 58 | value.LiftDistance + @event.LiftDistance); 59 | } 60 | 61 | private Value CreateValue(DateTime dateOfOccurrence) 62 | { 63 | var journeys = _journeysByDate.Get(dateOfOccurrence).Value; 64 | return new Value( 65 | journeys.Count, 66 | journeys.Sum(j => j.RouteDistance), 67 | 0, 68 | 0m); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/Messages/FailureMessages.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Application.QueryHandlers.Messages { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class FailureMessages { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal FailureMessages() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mors.Journeys.Application.QueryHandlers.Messages.FailureMessages", typeof(FailureMessages).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Cannot add key with null value.. 65 | /// 66 | internal static string CannotAddValueWithNullKey { 67 | get { 68 | return ResourceManager.GetString("CannotAddValueWithNullKey", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Nothing object has no value.. 74 | /// 75 | internal static string NothingObjectHasNoValue { 76 | get { 77 | return ResourceManager.GetString("NothingObjectHasNoValue", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/PassengerLiftCostCalculator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Mors.Journeys.Application; 3 | using Mors.Journeys.Data.Queries; 4 | using Mors.Journeys.Data.Queries.Dtos; 5 | using Mors.Journeys.Domain.Expenses.Capabilities; 6 | using Mors.Journeys.Domain.Expenses.Operations; 7 | using Mors.Journeys.Domain.Expenses.Policies; 8 | 9 | namespace Mors.Journeys.Application.QueryHandlers 10 | { 11 | internal sealed class PassengerLiftCostCalculator 12 | { 13 | private readonly IQueryDispatcher _queryDispatcher; 14 | 15 | public PassengerLiftCostCalculator(IQueryDispatcher queryDispatcher) 16 | { 17 | _queryDispatcher = queryDispatcher; 18 | } 19 | 20 | public PassengerLiftsCost Execute(GetCostOfPassengerLiftsInPeriodQuery query) 21 | { 22 | var journeysInPeriod = _queryDispatcher.Dispatch(new GetJourneysInPeriodQuery(query.Period)); 23 | var clerk = new Clerk(new EquallyDistributedCostPolicy(new Money(25m / 100m), query.PassengerId)); 24 | 25 | var journeys = BuildJourneys(journeysInPeriod); 26 | var liftsExpenses = clerk.CalculateExpenses(journeys); 27 | 28 | return new PassengerLiftsCost(liftsExpenses.TotalExpensesValue.Amount); 29 | } 30 | 31 | private static IEnumerable BuildJourneys( 32 | IEnumerable journeysInPeriod) 33 | { 34 | var journeyFactory = new JourneyFactory(); 35 | foreach (var journey in journeysInPeriod) 36 | { 37 | var journeyBuilder = journeyFactory.Create(journey.Id, journey.RouteDistance); 38 | foreach (var lift in journey.Lifts) 39 | { 40 | journeyBuilder.AddLift(lift.PassengerId, lift.Distance); 41 | } 42 | yield return journeyBuilder.Build(); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Mors.Journeys.Application/QueryHandlers/PersonView.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mors.Journeys.Application.QueryHandlers.Infrastructure.Views; 4 | using Mors.Journeys.Data.Events; 5 | using Mors.Journeys.Data.Queries; 6 | using Mors.Journeys.Data.Queries.Dtos; 7 | 8 | namespace Mors.Journeys.Application.QueryHandlers 9 | { 10 | internal sealed class PersonView 11 | { 12 | private readonly ValueSet _peopleNames = new ValueSet(personName => personName.OwnerId); 13 | private readonly ValueSet _peopleByName = new ValueSet(personName => personName.Name); 14 | 15 | public object Execute(GetPersonIdByNameQuery query) 16 | { 17 | var personName = _peopleByName.Get(query.PersonName, () => null); 18 | return personName == null ? null : personName.OwnerId; 19 | } 20 | 21 | public IEnumerable Execute(GetPeopleNamesQuery query) 22 | { 23 | return _peopleNames.Retrieve().ToList(); 24 | } 25 | 26 | public void Update(PersonCreatedEvent @event) 27 | { 28 | var personName = new PersonName(@event.PersonId, @event.PersonName); 29 | _peopleNames.Add(personName); 30 | _peopleByName.Add(personName); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Commands/AddJourneyWithLiftsCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using Mors.Journeys.Data.Commands.Dtos; 6 | 7 | namespace Mors.Journeys.Data.Commands 8 | { 9 | [DataContract] 10 | public sealed class AddJourneyWithLiftsCommand 11 | { 12 | [DataMember] 13 | public object JourneyId { get; private set; } 14 | 15 | [DataMember] 16 | public decimal RouteDistance { get; private set; } 17 | 18 | [DataMember] 19 | public DateTime DateOfOccurrence { get; private set; } 20 | 21 | [DataMember] 22 | public IReadOnlyList Lifts { get; private set; } 23 | 24 | public AddJourneyWithLiftsCommand( 25 | object journeyId, 26 | decimal routeDistance, 27 | DateTime dateOfOccurrence, 28 | IEnumerable lifts) 29 | { 30 | JourneyId = journeyId; 31 | RouteDistance = routeDistance; 32 | DateOfOccurrence = dateOfOccurrence; 33 | Lifts = lifts.ToList(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Commands/Dtos/Lift.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Commands.Dtos 4 | { 5 | [DataContract] 6 | public sealed class Lift 7 | { 8 | public Lift(string personName, decimal liftDistance) 9 | { 10 | PersonName = personName; 11 | LiftDistance = liftDistance; 12 | } 13 | 14 | [DataMember] 15 | public decimal LiftDistance { get; private set; } 16 | 17 | [DataMember] 18 | public string PersonName { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Events/JourneyCreatedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Mors.Journeys.Data.Events 5 | { 6 | [DataContract] 7 | public sealed class JourneyCreatedEvent 8 | { 9 | public JourneyCreatedEvent(object journeyId, DateTime dateOfOccurrence, decimal routeDistance) 10 | { 11 | JourneyId = journeyId; 12 | DateOfOccurrence = dateOfOccurrence; 13 | RouteDistance = routeDistance; 14 | } 15 | 16 | [DataMember] 17 | public object JourneyId { get; private set; } 18 | 19 | [DataMember] 20 | public DateTime DateOfOccurrence { get; private set; } 21 | 22 | [DataMember] 23 | public decimal RouteDistance { get; private set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Events/LiftAddedEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Events 4 | { 5 | [DataContract] 6 | public sealed class LiftAddedEvent 7 | { 8 | public LiftAddedEvent(object journeyId, object personId, decimal liftDistance) 9 | { 10 | JourneyId = journeyId; 11 | PersonId = personId; 12 | LiftDistance = liftDistance; 13 | } 14 | 15 | [DataMember] 16 | public object JourneyId { get; private set; } 17 | 18 | [DataMember] 19 | public object PersonId { get; private set; } 20 | 21 | [DataMember] 22 | public decimal LiftDistance { get; private set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Events/PersonCreatedEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Events 4 | { 5 | [DataContract] 6 | public sealed class PersonCreatedEvent 7 | { 8 | public PersonCreatedEvent(object id, string name) 9 | { 10 | PersonId = id; 11 | PersonName = name; 12 | } 13 | 14 | [DataMember] 15 | public object PersonId { get; private set; } 16 | 17 | [DataMember] 18 | public string PersonName { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/IHasId.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Data 2 | { 3 | public interface IHasId 4 | { 5 | object Id { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Data 2 | { 3 | public interface IQuery 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Mors.Journeys.Data.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Data definitions for Journeys application 6 | Copyright © Łukasz Mrozek 2019 7 | https://github.com/morsiu/Journeys 8 | Łukasz Mrozek 9 | 3.0.0 10 | true 11 | 12 | 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/Journey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | 5 | namespace Mors.Journeys.Data.Queries.Dtos 6 | { 7 | [DataContract] 8 | public sealed class Journey 9 | { 10 | public Journey(object id, DateTime dateOfOccurrence, decimal routeDistance, IReadOnlyCollection lifts) 11 | { 12 | Id = id; 13 | DateOfOccurrence = dateOfOccurrence; 14 | RouteDistance = routeDistance; 15 | Lifts = lifts; 16 | } 17 | 18 | [DataMember] 19 | public object Id { get; private set; } 20 | 21 | [DataMember] 22 | public DateTime DateOfOccurrence { get; private set; } 23 | 24 | [DataMember] 25 | public decimal RouteDistance { get; private set; } 26 | 27 | [DataMember] 28 | public IReadOnlyCollection Lifts { get; private set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneyWithLift.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Mors.Journeys.Data.Queries.Dtos 5 | { 6 | [DataContract] 7 | public sealed class JourneyWithLift 8 | { 9 | public JourneyWithLift( 10 | object journeyId, 11 | object passengerId, 12 | DateTime dateOfOccurrence, 13 | decimal routeDistance, 14 | string passengerName, 15 | decimal passengerLiftDistance) 16 | { 17 | JourneyId = journeyId; 18 | PassengerId = passengerId; 19 | DateOfOccurrence = dateOfOccurrence; 20 | RouteDistance = routeDistance; 21 | PassengerName = passengerName; 22 | PassengerLiftDistance = passengerLiftDistance; 23 | } 24 | 25 | [DataMember] 26 | public object JourneyId { get; private set; } 27 | 28 | [DataMember] 29 | public object PassengerId { get; private set; } 30 | 31 | [DataMember] 32 | public DateTime DateOfOccurrence { get; private set; } 33 | 34 | [DataMember] 35 | public decimal RouteDistance { get; private set; } 36 | 37 | [DataMember] 38 | public string PassengerName { get; private set; } 39 | 40 | [DataMember] 41 | public decimal PassengerLiftDistance { get; private set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Day.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public struct Day 7 | { 8 | public Day(int dayOfMonth) : this() 9 | { 10 | DayOfMonth = dayOfMonth; 11 | } 12 | 13 | [DataMember] 14 | public int DayOfMonth { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Fact.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public sealed class Fact 7 | { 8 | public Fact(Key key, Value value) 9 | { 10 | Key = key; 11 | Value = value; 12 | } 13 | 14 | [DataMember] 15 | public Key Key { get; private set; } 16 | 17 | [DataMember] 18 | public Value Value { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Key.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public struct Key 7 | { 8 | public Key(Passenger passenger, Month month, Day day) : this() 9 | { 10 | Passenger = passenger; 11 | Month = month; 12 | Day = day; 13 | } 14 | 15 | [DataMember] 16 | public Passenger Passenger { get; private set; } 17 | 18 | [DataMember] 19 | public Month Month { get; private set; } 20 | 21 | [DataMember] 22 | public Day Day { get; private set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Month.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public struct Month 7 | { 8 | public Month(int year, int monthOfYear) : this() 9 | { 10 | Year = year; 11 | MonthOfYear = monthOfYear; 12 | } 13 | 14 | [DataMember] 15 | public int MonthOfYear { get; private set; } 16 | 17 | [DataMember] 18 | public int Year { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Passenger.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public struct Passenger 7 | { 8 | public Passenger(object passengerId) : this() 9 | { 10 | Id = passengerId; 11 | } 12 | 13 | [DataMember] 14 | public object Id { get; private set; } 15 | 16 | public override bool Equals(object obj) 17 | { 18 | return obj is Passenger 19 | && Equals(((Passenger)obj).Id, Id); 20 | } 21 | 22 | public override int GetHashCode() 23 | { 24 | return Id == null ? 0 : Id.GetHashCode(); 25 | } 26 | 27 | public static bool operator ==(Passenger a, Passenger b) 28 | { 29 | return a.Equals(b); 30 | } 31 | 32 | public static bool operator !=(Passenger a, Passenger b) 33 | { 34 | return !a.Equals(b); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysByPassengerThenMonthThenDay/Value.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay 4 | { 5 | [DataContract] 6 | public struct Value 7 | { 8 | public Value(int journeyCount, decimal journeyDistance, int liftCount, decimal liftDistance) : this() 9 | { 10 | JourneyCount = journeyCount; 11 | JourneyDistance = journeyDistance; 12 | LiftCount = liftCount; 13 | LiftDistance = liftDistance; 14 | } 15 | 16 | [DataMember] 17 | public decimal LiftDistance { get; private set; } 18 | 19 | [DataMember] 20 | public int LiftCount { get; private set; } 21 | 22 | [DataMember] 23 | public decimal JourneyDistance { get; private set; } 24 | 25 | [DataMember] 26 | public int JourneyCount { get; private set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/JourneysOnDay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Mors.Journeys.Data.Queries.Dtos 5 | { 6 | [DataContract] 7 | public sealed class JourneysOnDay 8 | { 9 | public JourneysOnDay(DateTime day, int journeyCount, decimal totalRouteDistance, decimal totalLiftDistance) 10 | { 11 | Day = day; 12 | JourneyCount = journeyCount; 13 | TotalRouteDistance = totalRouteDistance; 14 | TotalLiftDistance = totalLiftDistance; 15 | } 16 | 17 | [DataMember] 18 | public DateTime Day { get; private set; } 19 | 20 | [DataMember] 21 | public int JourneyCount { get; private set; } 22 | 23 | [DataMember] 24 | public decimal TotalRouteDistance { get; private set; } 25 | 26 | [DataMember] 27 | public decimal TotalLiftDistance { get; private set; } 28 | } 29 | } -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/Lift.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos 4 | { 5 | [DataContract] 6 | public sealed class Lift 7 | { 8 | public Lift(object passengerId, decimal distance) 9 | { 10 | PassengerId = passengerId; 11 | Distance = distance; 12 | } 13 | 14 | [DataMember] 15 | public object PassengerId { get; private set; } 16 | 17 | [DataMember] 18 | public decimal Distance { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/PassengerLiftsCost.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos 4 | { 5 | [DataContract] 6 | public sealed class PassengerLiftsCost 7 | { 8 | public PassengerLiftsCost(decimal totalConst) 9 | { 10 | TotalCost = totalConst; 11 | } 12 | 13 | [DataMember] 14 | public decimal TotalCost { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/Period.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Mors.Journeys.Data.Queries.Dtos 5 | { 6 | [DataContract] 7 | public sealed class Period 8 | { 9 | public Period(DateTime start, DateTime end) 10 | { 11 | Start = start; 12 | End = end; 13 | } 14 | 15 | [DataMember] 16 | public DateTime Start { get; private set; } 17 | 18 | [DataMember] 19 | public DateTime End { get; private set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/Dtos/PersonName.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries.Dtos 4 | { 5 | [DataContract] 6 | public sealed class PersonName 7 | { 8 | public PersonName(object ownerId, string name) 9 | { 10 | OwnerId = ownerId; 11 | Name = name; 12 | } 13 | 14 | [DataMember] 15 | public object OwnerId { get; private set; } 16 | 17 | [DataMember] 18 | public string Name { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/GetCostOfPassengerLiftsInPeriodQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using Mors.Journeys.Data.Queries.Dtos; 3 | 4 | namespace Mors.Journeys.Data.Queries 5 | { 6 | [DataContract] 7 | public sealed class GetCostOfPassengerLiftsInPeriodQuery : IQuery 8 | { 9 | public GetCostOfPassengerLiftsInPeriodQuery(object passengerId, Period period) 10 | { 11 | Period = period; 12 | PassengerId = passengerId; 13 | } 14 | 15 | [DataMember] 16 | public object PassengerId { get; private set; } 17 | 18 | [DataMember] 19 | public Period Period { get; private set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/GetJourneysByPassengerThenMonthThenDayQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | using Mors.Journeys.Data.Queries.Dtos.JourneysByPassengerThenMonthThenDay; 4 | 5 | namespace Mors.Journeys.Data.Queries 6 | { 7 | [DataContract] 8 | public sealed class GetJourneysByPassengerThenMonthThenDayQuery : IQuery> 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/GetJourneysInPeriodQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | using Mors.Journeys.Data.Queries.Dtos; 4 | 5 | namespace Mors.Journeys.Data.Queries 6 | { 7 | [DataContract] 8 | public sealed class GetJourneysInPeriodQuery : IQuery> 9 | { 10 | public GetJourneysInPeriodQuery(Period period) 11 | { 12 | Period = period; 13 | } 14 | 15 | [DataMember] 16 | public Period Period { get; private set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/GetPeopleNamesQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | using Mors.Journeys.Data.Queries.Dtos; 4 | 5 | namespace Mors.Journeys.Data.Queries 6 | { 7 | [DataContract] 8 | public sealed class GetPeopleNamesQuery : IQuery> 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Data/Queries/GetPersonIdByNameQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Mors.Journeys.Data.Queries 4 | { 5 | [DataContract] 6 | public sealed class GetPersonIdByNameQuery : IQuery 7 | { 8 | [DataMember] 9 | public string PersonName { get; private set; } 10 | 11 | public GetPersonIdByNameQuery(string personName) 12 | { 13 | PersonName = personName; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses.Test/Capabilities/ExpenseListTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Mors.Journeys.Domain.Expenses.Capabilities; 3 | using Mors.Journeys.Domain.Test; 4 | 5 | namespace Mors.Journeys.Domain.Expenses.Test.Capabilities 6 | { 7 | [TestClass] 8 | public sealed class ExpenseListTest 9 | { 10 | private static object Id = new Id(1); 11 | 12 | [TestMethod] 13 | public void GetExpenseShouldReturnExpenseAfterItWasAddedWithAddExpenseUsingSameId() 14 | { 15 | var list = new ExpenseList(); 16 | var expenseValue = new Money(10m); 17 | 18 | list.AddExpense(new Expense(Id, expenseValue)); 19 | var result = list.GetExpense(Id); 20 | 21 | Assert.AreEqual(expenseValue, result.Value); 22 | } 23 | 24 | [TestMethod] 25 | public void AddExpenseShouldIncreaseExpenseAfterItWasAlreadyAdded() 26 | { 27 | var list = new ExpenseList(); 28 | var expenseValue = new Money(10m); 29 | 30 | list.AddExpense(new Expense(Id, expenseValue)); 31 | list.AddExpense(new Expense(Id, expenseValue)); 32 | var updatedExpense = list.GetExpense(Id); 33 | 34 | Assert.AreEqual(new Money(20m), updatedExpense.Value); 35 | } 36 | 37 | [TestMethod] 38 | public void GetExpenseShouldReturnZeroExpenseAfterConstruction() 39 | { 40 | var list = new ExpenseList(); 41 | 42 | var result = list.GetExpense(Id); 43 | 44 | Assert.AreEqual(new Money(), result.Value); 45 | } 46 | 47 | [TestMethod] 48 | public void TotalExpenseShouldBeZeroAfterConstruction() 49 | { 50 | var list = new ExpenseList(); 51 | 52 | var total = list.TotalExpensesValue; 53 | 54 | Assert.AreEqual(new Money(), total); 55 | } 56 | 57 | [TestMethod] 58 | public void AddExpenseShouldIncreaseTotalWithTheExpense() 59 | { 60 | var list = new ExpenseList(); 61 | var expense = new Money(10m); 62 | 63 | list.AddExpense(new Expense(Id, expense)); 64 | var totalExpense = list.TotalExpensesValue; 65 | 66 | Assert.AreEqual(expense, totalExpense); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses.Test/Capabilities/LiftIdTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Mors.Journeys.Domain.Expenses.Capabilities; 3 | using Mors.Journeys.Domain.Test; 4 | 5 | namespace Mors.Journeys.Domain.Expenses.Test.Capabilities 6 | { 7 | [TestClass] 8 | public sealed class LiftIdTest 9 | { 10 | [TestMethod] 11 | public void EqualsShouldReturnTrueForInstancesWithSameJourneyAndPersonIds() 12 | { 13 | var a = new LiftId(new Id(0), new Id(1)); 14 | var b = new LiftId(new Id(0), new Id(1)); 15 | 16 | var result = a.Equals(b); 17 | 18 | Assert.AreEqual(true, result); 19 | } 20 | 21 | [TestMethod] 22 | public void EqualsShouldReturnFalseForInstancesWithSameJourneyButDifferentPersonIds() 23 | { 24 | var a = new LiftId(new Id(0), new Id(1)); 25 | var b = new LiftId(new Id(0), new Id(2)); 26 | 27 | var result = a.Equals(b); 28 | 29 | Assert.AreEqual(false, result); 30 | } 31 | 32 | [TestMethod] 33 | public void EqualsShouldReturnFalseForInstancesWithSamePersonButDifferentJourneyIds() 34 | { 35 | var a = new LiftId(new Id(0), new Id(2)); 36 | var b = new LiftId(new Id(1), new Id(2)); 37 | 38 | var result = a.Equals(b); 39 | 40 | Assert.AreEqual(false, result); 41 | } 42 | 43 | [TestMethod] 44 | public void EqualsShouldReturnFalseForInstancesWhereSecondIsNotLiftId() 45 | { 46 | var a = new LiftId(new Id(0), new Id(1)); 47 | var b = new Id(0); 48 | 49 | var result = a.Equals(b); 50 | 51 | Assert.AreEqual(false, result); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses.Test/Capabilities/MoneyTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Mors.Journeys.Domain.Expenses.Capabilities; 3 | 4 | namespace Mors.Journeys.Domain.Expenses.Test.Capabilities 5 | { 6 | [TestClass] 7 | public sealed class MoneyTest 8 | { 9 | [TestMethod] 10 | public void ShouldHaveAmountGivenAtConstruction() 11 | { 12 | var money = new Money(10m); 13 | 14 | Assert.AreEqual(10, money.Amount); 15 | } 16 | 17 | [TestMethod] 18 | public void ShouldHaveZeroAmountAfterDefaultConstruction() 19 | { 20 | var money = new Money(); 21 | 22 | Assert.AreEqual(0, money.Amount); 23 | } 24 | 25 | [TestMethod] 26 | public void AddingMoneyShouldResultInMoneyWithAmountBeingSumOfAddentsAmounts() 27 | { 28 | var a = new Money(10m); 29 | var b = new Money(20m); 30 | 31 | var c = a + b; 32 | 33 | Assert.AreEqual(30, c.Amount); 34 | } 35 | 36 | [TestMethod] 37 | public void MultiplyingMoneyByNumberShouldResultInMoneyWithAmountMultipliedByThatNumber() 38 | { 39 | var a = new Money(10m); 40 | 41 | var c = a * 3; 42 | 43 | Assert.AreEqual(30, c.Amount); 44 | } 45 | 46 | [TestMethod] 47 | public void MultiplyingNumberByMoneyShouldResultInMoneyWithAmountMultipliedByThatNumber() 48 | { 49 | var a = new Money(10m); 50 | 51 | var c = 3 * a; 52 | 53 | Assert.AreEqual(30, c.Amount); 54 | } 55 | 56 | [TestMethod] 57 | public void EqualsShouldReturnTrueForMoneysWithSameAmount() 58 | { 59 | var a = new Money(10m); 60 | var b = new Money(10m); 61 | 62 | var result = a.Equals(b); 63 | 64 | Assert.AreEqual(true, result); 65 | } 66 | 67 | [TestMethod] 68 | public void EqualsShouldReturnFalseForMoneysWithDifferentAmount() 69 | { 70 | var a = new Money(10m); 71 | var b = new Money(5m); 72 | 73 | var result = a.Equals(b); 74 | 75 | Assert.AreEqual(false, result); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses.Test/Mors.Journeys.Domain.Expenses.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Distance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Domain.Infrastructure.Markers; 3 | 4 | namespace Mors.Journeys.Domain.Expenses.Capabilities 5 | { 6 | [ValueObject] 7 | public struct Distance : IComparable 8 | { 9 | private readonly decimal _amount; 10 | 11 | public Distance(decimal amount) 12 | { 13 | _amount = amount; 14 | } 15 | 16 | public decimal Amount { get { return _amount; } } 17 | 18 | public static bool operator==(Distance left, Distance right) 19 | { 20 | return left.Equals(right); 21 | } 22 | 23 | public static bool operator!=(Distance left, Distance right) 24 | { 25 | return !(left == right); 26 | } 27 | 28 | public static bool operator<(Distance left, Distance right) 29 | { 30 | return left._amount < right._amount; 31 | } 32 | 33 | public static bool operator>(Distance left, Distance right) 34 | { 35 | return left._amount > right._amount; 36 | } 37 | 38 | public static Distance operator-(Distance a, Distance b) 39 | { 40 | return new Distance(Math.Abs(a.Amount - b.Amount)); 41 | } 42 | 43 | public override bool Equals(object obj) 44 | { 45 | return obj is Distance && Equals((Distance)obj); 46 | } 47 | 48 | public override int GetHashCode() 49 | { 50 | return _amount.GetHashCode(); 51 | } 52 | 53 | public bool Equals(Distance other) 54 | { 55 | return _amount == other._amount; 56 | } 57 | 58 | public int CompareTo(Distance distance) 59 | { 60 | return _amount.CompareTo(distance._amount); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Expense.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Infrastructure.Markers; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | [ValueObject] 6 | public struct Expense 7 | { 8 | private readonly object _subjectId; 9 | private readonly Money _value; 10 | 11 | public Expense(object subjectId, Money value) 12 | { 13 | _subjectId = subjectId; 14 | _value = value; 15 | } 16 | 17 | public object SubjectId { get { return _subjectId; } } 18 | 19 | public Money Value { get { return _value; } } 20 | 21 | public Expense Increase(Money value) 22 | { 23 | return new Expense(_subjectId, _value + value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/ExpenseList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | public sealed class ExpenseList 6 | { 7 | private readonly Dictionary _expenseValues = new Dictionary(); 8 | 9 | public Money TotalExpensesValue { get; private set; } 10 | 11 | public void AddExpense(Expense expense) 12 | { 13 | var previousExpense = GetExpense(expense.SubjectId); 14 | StoreExpense(previousExpense.Increase(expense.Value)); 15 | IncreaseTotalExpensesValue(expense.Value); 16 | } 17 | 18 | public Expense GetExpense(object subjectId) 19 | { 20 | Money expenseValue; 21 | _expenseValues.TryGetValue(subjectId, out expenseValue); 22 | return new Expense(subjectId, expenseValue); 23 | } 24 | 25 | private void StoreExpense(Expense expense) 26 | { 27 | _expenseValues[expense.SubjectId] = expense.Value; 28 | } 29 | 30 | private void IncreaseTotalExpensesValue(Money expense) 31 | { 32 | TotalExpensesValue += expense; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/IJourneyEvent.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | internal interface IJourneyEvent 6 | { 7 | void Visit(IJourneyVisitor visitor); 8 | 9 | RoutePoint Point { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/IJourneyVisitor.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | internal interface IJourneyVisitor 6 | { 7 | void Visit(Drive drive); 8 | 9 | void Visit(PassengerExit exit); 10 | 11 | void Visit(PassengerPickup pickup); 12 | 13 | void Visit(JourneyStart start); 14 | 15 | void Visit(JourneyFinish finish); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys; 3 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events; 4 | using Mors.Journeys.Domain.Infrastructure.Markers; 5 | 6 | namespace Mors.Journeys.Domain.Expenses.Capabilities 7 | { 8 | [Entity] 9 | public sealed class Journey 10 | { 11 | private readonly Route _route; 12 | private readonly object _id; 13 | 14 | internal Journey(object journeyId, Distance routeDistance, IEnumerable journeyLifts) 15 | { 16 | _id = journeyId; 17 | var events = CreateEvents(routeDistance, journeyLifts); 18 | _route = new Route(events); 19 | } 20 | 21 | public object Id { get { return _id; } } 22 | 23 | private static IReadOnlyCollection CreateEvents(Distance routeDistance, IEnumerable lifts) 24 | { 25 | var events = new List(); 26 | events.Add(new JourneyStart()); 27 | foreach (var lift in lifts) 28 | { 29 | events.Add(new PassengerPickup(lift.PassengerId, lift.Distance.From)); 30 | events.Add(new PassengerExit(lift.PassengerId, lift.Distance.To)); 31 | } 32 | events.Add(new JourneyFinish(new RoutePoint(routeDistance))); 33 | events.Sort((a, b) => a.Point.CompareTo(b.Point)); 34 | return events; 35 | } 36 | 37 | internal void Visit(IJourneyVisitor visitor) 38 | { 39 | _route.Replay(visitor); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Events/Drive.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events 2 | { 3 | internal sealed class Drive 4 | { 5 | public Drive(RouteDistance distance) 6 | { 7 | Distance = distance; 8 | } 9 | 10 | public RouteDistance Distance { get; private set; } 11 | 12 | public void Visit(IJourneyVisitor visitor) 13 | { 14 | visitor.Visit(this); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Events/JourneyFinish.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events 2 | { 3 | internal sealed class JourneyFinish : IJourneyEvent 4 | { 5 | private readonly RoutePoint _endPoint; 6 | 7 | public JourneyFinish(RoutePoint endPoint) 8 | { 9 | _endPoint = endPoint; 10 | } 11 | 12 | public void Visit(IJourneyVisitor visitor) 13 | { 14 | visitor.Visit(this); 15 | } 16 | 17 | public RoutePoint Point 18 | { 19 | get { return _endPoint; } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Events/JourneyStart.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events 2 | { 3 | internal sealed class JourneyStart : IJourneyEvent 4 | { 5 | public void Visit(IJourneyVisitor visitor) 6 | { 7 | visitor.Visit(this); 8 | } 9 | 10 | public RoutePoint Point { get { return new RoutePoint(); } } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Events/PassengerExit.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events 2 | { 3 | internal sealed class PassengerExit : IJourneyEvent 4 | { 5 | private readonly object _passengerId; 6 | private readonly RoutePoint _point; 7 | 8 | public PassengerExit(object passengerId, RoutePoint point) 9 | { 10 | _passengerId = passengerId; 11 | _point = point; 12 | } 13 | 14 | public RoutePoint Point { get { return _point; } } 15 | 16 | public object PassengerId { get { return _passengerId; } } 17 | 18 | public void Visit(IJourneyVisitor visitor) 19 | { 20 | visitor.Visit(this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Events/PassengerPickup.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events 2 | { 3 | internal sealed class PassengerPickup : IJourneyEvent 4 | { 5 | private readonly object _passengerId; 6 | private readonly RoutePoint _point; 7 | 8 | public PassengerPickup(object passengerId, RoutePoint point) 9 | { 10 | _passengerId = passengerId; 11 | _point = point; 12 | } 13 | 14 | public object PassengerId { get { return _passengerId; } } 15 | 16 | public RoutePoint Point { get { return _point; } } 17 | 18 | public void Visit(IJourneyVisitor visitor) 19 | { 20 | visitor.Visit(this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/Route.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events; 4 | 5 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys 6 | { 7 | internal sealed class Route 8 | { 9 | private readonly IReadOnlyCollection _events; 10 | 11 | public Route(IReadOnlyCollection events) 12 | { 13 | _events = events.ToList(); 14 | } 15 | 16 | public void Replay(IJourneyVisitor visitor) 17 | { 18 | IJourneyEvent previousEvent = null; 19 | using (var events = _events.GetEnumerator()) 20 | { 21 | while (events.MoveNext()) 22 | { 23 | var currentEvent = events.Current; 24 | DriveBetweenEvents(visitor, previousEvent, currentEvent); 25 | currentEvent.Visit(visitor); 26 | previousEvent = currentEvent; 27 | } 28 | } 29 | } 30 | 31 | private void DriveBetweenEvents(IJourneyVisitor visitor, IJourneyEvent previous, IJourneyEvent current) 32 | { 33 | if (previous == null) return; 34 | if (previous.Point < current.Point) 35 | { 36 | visitor.Visit(CreateDrive(previous, current)); 37 | } 38 | } 39 | 40 | private Drive CreateDrive(IJourneyEvent last, IJourneyEvent current) 41 | { 42 | return new Drive(new RouteDistance(last.Point, current.Point)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/RouteDistance.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Infrastructure.Markers; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys 4 | { 5 | [ValueObject] 6 | internal struct RouteDistance 7 | { 8 | private readonly RoutePoint _from; 9 | private readonly RoutePoint _to; 10 | 11 | public RouteDistance(RoutePoint from, RoutePoint to) 12 | { 13 | _from = from; 14 | _to = to; 15 | } 16 | 17 | public RoutePoint From { get { return _from; } } 18 | 19 | public RoutePoint To { get { return _to; } } 20 | 21 | public Distance Length { get { return _to - _from; } } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Journeys/RoutePoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Domain.Infrastructure.Markers; 3 | 4 | namespace Mors.Journeys.Domain.Expenses.Capabilities.Journeys 5 | { 6 | [ValueObject] 7 | internal struct RoutePoint : IComparable 8 | { 9 | private readonly Distance _distanceFromStart; 10 | 11 | public RoutePoint(Distance distanceFromStart) 12 | { 13 | _distanceFromStart = distanceFromStart; 14 | } 15 | 16 | public Distance DistanceFromStart { get { return _distanceFromStart; } } 17 | 18 | public int CompareTo(RoutePoint other) 19 | { 20 | return _distanceFromStart.CompareTo(other._distanceFromStart); 21 | } 22 | 23 | public static bool operator==(RoutePoint a, RoutePoint b) 24 | { 25 | return a.Equals(b); 26 | } 27 | 28 | public static bool operator!=(RoutePoint a, RoutePoint b) 29 | { 30 | return !a.Equals(b); 31 | } 32 | 33 | public static bool operator<(RoutePoint a, RoutePoint b) 34 | { 35 | return a.DistanceFromStart < b.DistanceFromStart; 36 | } 37 | 38 | public static bool operator>(RoutePoint a, RoutePoint b) 39 | { 40 | return a.DistanceFromStart > b.DistanceFromStart; 41 | } 42 | 43 | public static Distance operator-(RoutePoint a, RoutePoint b) 44 | { 45 | return a.DistanceFromStart - b.DistanceFromStart; 46 | } 47 | 48 | public bool Equals(RoutePoint other) 49 | { 50 | return _distanceFromStart == other._distanceFromStart; 51 | } 52 | 53 | public override bool Equals(object obj) 54 | { 55 | return obj is RoutePoint && Equals((RoutePoint)obj); 56 | } 57 | 58 | public override int GetHashCode() 59 | { 60 | return _distanceFromStart.GetHashCode(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Lift.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | internal struct Lift 6 | { 7 | private readonly object _passengerId; 8 | private readonly RouteDistance _distance; 9 | 10 | public Lift(object passengerId, RouteDistance liftDistance) 11 | { 12 | _passengerId = passengerId; 13 | _distance = liftDistance; 14 | } 15 | 16 | public object PassengerId { get { return _passengerId; } } 17 | 18 | public RouteDistance Distance { get { return _distance; } } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/LiftId.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Expenses.Capabilities 2 | { 3 | public struct LiftId 4 | { 5 | private readonly object _journeyId; 6 | private readonly object _personId; 7 | 8 | public LiftId(object journeyId, object personId) 9 | { 10 | _journeyId = journeyId; 11 | _personId = personId; 12 | } 13 | 14 | public override bool Equals(object other) 15 | { 16 | return other is LiftId && Equals((LiftId)other); 17 | } 18 | 19 | public override int GetHashCode() 20 | { 21 | return unchecked(_journeyId.GetHashCode() * 397 ^ _personId.GetHashCode()); 22 | } 23 | 24 | private bool Equals(LiftId other) 25 | { 26 | return Equals(_personId, other._personId) 27 | && Equals(_journeyId, other._journeyId); 28 | } 29 | 30 | public static bool operator ==(LiftId a, LiftId b) 31 | { 32 | return a.Equals(b); 33 | } 34 | 35 | public static bool operator !=(LiftId a, LiftId b) 36 | { 37 | return !a.Equals(b); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Capabilities/Money.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Infrastructure.Markers; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Capabilities 4 | { 5 | [ValueObject] 6 | public struct Money 7 | { 8 | private readonly decimal _amount; 9 | 10 | public Money(decimal amount) 11 | : this() 12 | { 13 | _amount = amount; 14 | } 15 | 16 | public decimal Amount { get { return _amount; } } 17 | 18 | public static Money operator+(Money a, Money b) 19 | { 20 | return new Money(a.Amount + b.Amount); 21 | } 22 | 23 | public static Money operator *(Money a, decimal b) 24 | { 25 | return new Money(a.Amount * b); 26 | } 27 | 28 | public static Money operator *(decimal a, Money b) 29 | { 30 | return new Money(a * b.Amount); 31 | } 32 | 33 | public static Money operator /(Money a, decimal b) 34 | { 35 | return new Money(a.Amount / b); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Messages.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Domain.Expenses { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Messages { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Messages() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mors.Journeys.Domain.Expenses.Messages", typeof(Messages).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Journey already contains lift for that person.. 65 | /// 66 | public static string JourneyAlreadyContainsLiftForThatPerson { 67 | get { 68 | return ResourceManager.GetString("JourneyAlreadyContainsLiftForThatPerson", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Mors.Journeys.Domain.Expenses.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 1.0.0 6 | Copyright © Łukasz Mrozek 2019 7 | Łukasz Mrozek 8 | Expenses domain for journeys application 9 | https://github.com/morsiu/Journeys 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Operations/Clerk.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Mors.Journeys.Domain.Expenses.Capabilities; 3 | using Mors.Journeys.Domain.Expenses.Policies; 4 | using Mors.Journeys.Domain.Infrastructure.Markers; 5 | 6 | namespace Mors.Journeys.Domain.Expenses.Operations 7 | { 8 | [Service] 9 | public sealed class Clerk 10 | { 11 | private readonly IJourneyCostPolicy _journeyCostPolicy; 12 | 13 | public Clerk(IJourneyCostPolicy journeyCostPolicy) 14 | { 15 | _journeyCostPolicy = journeyCostPolicy; 16 | } 17 | 18 | public ExpenseList CalculateExpenses(IEnumerable journeys) 19 | { 20 | var expenseList = new ExpenseList(); 21 | foreach (var journey in journeys) 22 | { 23 | var journeyExpense = _journeyCostPolicy.CalculateCost(journey); 24 | expenseList.AddExpense(journeyExpense); 25 | } 26 | return expenseList; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Operations/JourneyBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mors.Journeys.Domain.Expenses.Capabilities; 5 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys; 6 | using Mors.Journeys.Domain.Infrastructure.Markers; 7 | 8 | namespace Mors.Journeys.Domain.Expenses.Operations 9 | { 10 | [Factory] 11 | public sealed class JourneyBuilder 12 | { 13 | private readonly object _journeyId; 14 | private readonly Distance _routeDistance; 15 | private readonly Dictionary _liftDistancesByPassengerId = new Dictionary(); 16 | 17 | public JourneyBuilder(object journeyId, decimal routeDistance) 18 | { 19 | _journeyId = journeyId; 20 | _routeDistance = new Distance(routeDistance); 21 | } 22 | 23 | public void AddLift(object passengerId, decimal distance) 24 | { 25 | if (_liftDistancesByPassengerId.ContainsKey(passengerId)) 26 | { 27 | throw new ArgumentException(Messages.JourneyAlreadyContainsLiftForThatPerson, "passengerId"); 28 | } 29 | _liftDistancesByPassengerId[passengerId] = new Distance(distance); 30 | } 31 | 32 | public Journey Build() 33 | { 34 | var lifts = _liftDistancesByPassengerId.Select(BuildLift); 35 | return new Journey(_journeyId, _routeDistance, lifts); 36 | } 37 | 38 | private static Lift BuildLift(KeyValuePair passengerIdAndDistance) 39 | { 40 | var liftDistance = new RouteDistance(new RoutePoint(), new RoutePoint(passengerIdAndDistance.Value)); 41 | return new Lift(passengerIdAndDistance.Key, liftDistance); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Operations/JourneyFactory.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Infrastructure.Markers; 2 | 3 | namespace Mors.Journeys.Domain.Expenses.Operations 4 | { 5 | [Factory] 6 | public sealed class JourneyFactory 7 | { 8 | public JourneyBuilder Create(object journeyId, decimal routeDistance) 9 | { 10 | return new JourneyBuilder(journeyId, routeDistance); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Policies/EquallyDistributedCostPolicy.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Expenses.Capabilities; 2 | using Mors.Journeys.Domain.Expenses.Capabilities.Journeys.Events; 3 | using Mors.Journeys.Domain.Infrastructure.Markers; 4 | 5 | namespace Mors.Journeys.Domain.Expenses.Policies 6 | { 7 | [Policy] 8 | public sealed class EquallyDistributedCostPolicy : IJourneyCostPolicy 9 | { 10 | private readonly object _passengerId; 11 | private readonly Money _journeyCostPerKilometer; 12 | 13 | public EquallyDistributedCostPolicy(Money journeyCostPerKilometer, object passengerId) 14 | { 15 | _journeyCostPerKilometer = journeyCostPerKilometer; 16 | _passengerId = passengerId; 17 | } 18 | 19 | public Expense CalculateCost(Journey journey) 20 | { 21 | var visitor = new JourneyVisitor(_journeyCostPerKilometer, _passengerId); 22 | journey.Visit(visitor); 23 | var liftId = new LiftId(journey.Id, _passengerId); 24 | return new Expense(liftId, visitor.PassengerCost); 25 | } 26 | 27 | private class JourneyVisitor : IJourneyVisitor 28 | { 29 | private readonly object _passengerId; 30 | private readonly Money _journeyCostPerKilometer; 31 | private bool _isPassengerOnBoard; 32 | private int _onBoardPersonCount; 33 | private Money _passengerCost; 34 | 35 | public JourneyVisitor(Money journeyCostPerKilometer, object passengerId) 36 | { 37 | _journeyCostPerKilometer = journeyCostPerKilometer; 38 | _passengerId = passengerId; 39 | _isPassengerOnBoard = false; 40 | _onBoardPersonCount = 1; // Count the driver 41 | _passengerCost = new Money(); 42 | } 43 | 44 | private void IncreasePassengerCost(Distance driveDistance) 45 | { 46 | var driveCost = _journeyCostPerKilometer * driveDistance.Amount; 47 | var passengerCost = driveCost / _onBoardPersonCount; 48 | _passengerCost += passengerCost; 49 | } 50 | 51 | public Money PassengerCost { get { return _passengerCost; } } 52 | 53 | void IJourneyVisitor.Visit(Drive drive) 54 | { 55 | if (_isPassengerOnBoard) IncreasePassengerCost(drive.Distance.Length); 56 | } 57 | 58 | void IJourneyVisitor.Visit(PassengerExit exit) 59 | { 60 | _onBoardPersonCount -= 1; 61 | if (exit.PassengerId.Equals(_passengerId)) _isPassengerOnBoard = false; 62 | } 63 | 64 | void IJourneyVisitor.Visit(PassengerPickup pickup) 65 | { 66 | _onBoardPersonCount += 1; 67 | if (pickup.PassengerId.Equals(_passengerId)) _isPassengerOnBoard = true; 68 | } 69 | 70 | void IJourneyVisitor.Visit(JourneyStart start) 71 | { 72 | } 73 | 74 | void IJourneyVisitor.Visit(JourneyFinish finish) 75 | { 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Policies/IJourneyCostPolicy.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Expenses.Capabilities; 2 | using Mors.Journeys.Domain.Infrastructure.Markers; 3 | 4 | namespace Mors.Journeys.Domain.Expenses.Policies 5 | { 6 | [Policy] 7 | public interface IJourneyCostPolicy 8 | { 9 | Expense CalculateCost(Journey journey); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Expenses/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Mors.Journeys.Domain.Expenses.Test")] -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Collections/ImmutableList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace Mors.Journeys.Domain.Infrastructure.Collections 5 | { 6 | public sealed class ImmutableList : IEnumerable 7 | { 8 | private readonly Item _head; 9 | public static readonly ImmutableList Empty = new ImmutableList(null); 10 | 11 | private ImmutableList(Item head) 12 | { 13 | _head = head; 14 | } 15 | 16 | public ImmutableList Add(T value) 17 | { 18 | var item = new Item(value, _head); 19 | return new ImmutableList(item); 20 | } 21 | 22 | public IEnumerator GetEnumerator() 23 | { 24 | var item = _head; 25 | while (item != null) 26 | { 27 | yield return item.Value; 28 | item = item.Next; 29 | } 30 | } 31 | 32 | IEnumerator IEnumerable.GetEnumerator() 33 | { 34 | return GetEnumerator(); 35 | } 36 | 37 | private class Item 38 | { 39 | public Item(T value, Item next) 40 | { 41 | Value = value; 42 | Next = next; 43 | } 44 | 45 | public T Value { get; private set; } 46 | public Item Next { get; private set; } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Exceptions/InvariantViolationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Exceptions 4 | { 5 | /// 6 | /// Exceptions used by bussiness objects to report failure of performed operation because of invariant violation. 7 | /// 8 | [Serializable] 9 | public sealed class InvariantViolationException : Exception 10 | { 11 | public InvariantViolationException(string message) 12 | : base(message) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/AggregateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public sealed class AggregateAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/EntityAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public sealed class EntityAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/FactoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public sealed class FactoryAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/PolicyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] 6 | public sealed class PolicyAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/RepositoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Interface)] 6 | public sealed class RepositoryAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/ServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] 6 | public sealed class ServiceAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Markers/ValueObjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mors.Journeys.Domain.Infrastructure.Markers 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 6 | public sealed class ValueObjectAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Infrastructure/Mors.Journeys.Domain.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 1.0.0 6 | Copyright © Łukasz Mrozek 2019 7 | Łukasz Mrozek 8 | Infrastructure for domains of journeys application 9 | https://github.com/morsiu/Journeys 10 | true 11 | 12 | 13 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys.Test/Capabilities/DistanceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Mors.Journeys.Domain.Journeys.Capabilities; 4 | 5 | namespace Mors.Journeys.Domain.Journeys.Test.Capabilities 6 | { 7 | [TestClass] 8 | public sealed class DistanceTest 9 | { 10 | [TestMethod] 11 | [ExpectedException(typeof(ArgumentException))] 12 | public void ShouldNotConstructWithNegativeAmount() 13 | { 14 | var negativeAmount = -4m; 15 | 16 | new Distance(negativeAmount, DistanceUnit.Kilometer); 17 | } 18 | 19 | [TestMethod] 20 | public void DistanceWithEqualAmountsInKilometersShouldBeEqual() 21 | { 22 | var distance = new Distance(2m, DistanceUnit.Kilometer); 23 | 24 | Assert.AreEqual(true, distance.Equals(distance)); 25 | } 26 | 27 | [TestMethod] 28 | public void DistanceWithSmallerAmountInKilometersShouldBeSmaller() 29 | { 30 | var smallerDistance = new Distance(3m, DistanceUnit.Kilometer); 31 | var largerDistance = new Distance(5m, DistanceUnit.Kilometer); 32 | 33 | Assert.IsTrue(smallerDistance < largerDistance); 34 | } 35 | 36 | [TestMethod] 37 | public void DistanceWithLargerAmountInKilometersShouldBeLarger() 38 | { 39 | var largerDistance = new Distance(5m, DistanceUnit.Kilometer); 40 | var smallerDistance = new Distance(3m, DistanceUnit.Kilometer); 41 | 42 | Assert.IsTrue(largerDistance > smallerDistance); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys.Test/Capabilities/LiftTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Mors.Journeys.Domain.Journeys.Capabilities; 3 | using Mors.Journeys.Domain.Test; 4 | 5 | namespace Mors.Journeys.Domain.Journeys.Test.Capabilities 6 | { 7 | [TestClass] 8 | public sealed class LiftTest 9 | { 10 | private static readonly object PersonId = new Id(0); 11 | private static readonly object AnotherPersonId = new Id(1); 12 | 13 | [TestMethod] 14 | public void LiftWithPersonIdShouldBeForPersonWithThatId() 15 | { 16 | var lift = new Lift(PersonId); 17 | 18 | Assert.IsTrue(lift.IsForPerson(PersonId)); 19 | } 20 | 21 | [TestMethod] 22 | public void LiftWithPersonIdShouldNotBeForPersonWithDifferentId() 23 | { 24 | var lift = new Lift(PersonId); 25 | 26 | Assert.IsFalse(lift.IsForPerson(AnotherPersonId)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys.Test/Mors.Journeys.Domain.Journeys.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys.Test/Operations/JourneyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Mors.Journeys.Data.Events; 4 | using Mors.Journeys.Domain.Infrastructure.Exceptions; 5 | using Mors.Journeys.Domain.Journeys.Capabilities; 6 | using Mors.Journeys.Domain.Journeys.Operations; 7 | using Mors.Journeys.Domain.Test; 8 | 9 | namespace Mors.Journeys.Domain.Journeys.Test.Operations 10 | { 11 | [TestClass] 12 | public sealed class JourneyTest 13 | { 14 | private static readonly object JourneyId = new Id(0); 15 | private static readonly object PersonId = new Id(0); 16 | private EventBus _eventBus; 17 | 18 | [TestInitialize] 19 | public void TestInitialize() 20 | { 21 | _eventBus = new EventBus(); 22 | } 23 | 24 | [TestMethod] 25 | public void ShouldPublishEventWhenAddingLift() 26 | { 27 | var journey = new Journey(JourneyId, new DateTime(), new Distance(10m, DistanceUnit.Kilometer), _eventBus.Publish); 28 | var liftDistance = new Distance(5m, DistanceUnit.Kilometer); 29 | 30 | var eventMatcher = _eventBus.Listen(() => 31 | { 32 | journey.AddLift(PersonId, liftDistance); 33 | }); 34 | 35 | eventMatcher.AssertReceivedOneEvent( 36 | evt => evt.LiftDistance == liftDistance && 37 | evt.PersonId.Equals(PersonId)); 38 | } 39 | 40 | [TestMethod] 41 | public void ShouldPublishEventWhenCreatingJourney() 42 | { 43 | var dateOfOccurence = new DateTime(); 44 | var routeDistance = new Distance(10m, DistanceUnit.Kilometer); 45 | 46 | var eventMatcher = _eventBus.Listen(() => 47 | { 48 | new Journey(JourneyId, dateOfOccurence, routeDistance, _eventBus.Publish); 49 | }); 50 | 51 | eventMatcher.AssertReceivedOneEvent( 52 | evt => evt.DateOfOccurrence == dateOfOccurence && 53 | evt.JourneyId == JourneyId && 54 | evt.RouteDistance == routeDistance); 55 | } 56 | 57 | [TestMethod] 58 | [ExpectedException(typeof(InvariantViolationException))] 59 | public void ShouldReportInvariantViolationWhenAddingLiftWithDistanceLargerThanRouteDistance() 60 | { 61 | var journey = new Journey(JourneyId, new DateTime(), new Distance(20m, DistanceUnit.Kilometer), _eventBus.Publish); 62 | 63 | journey.AddLift(PersonId, new Distance(40m, DistanceUnit.Kilometer)); 64 | } 65 | 66 | [TestMethod] 67 | [ExpectedException(typeof(InvariantViolationException))] 68 | public void ShouldReportInvariantViolationWhenGivenAJourneyWithLiftANewLiftIsAddedWithSamePerson() 69 | { 70 | var journey = new Journey(JourneyId, new DateTime(), new Distance(20m, DistanceUnit.Kilometer), _eventBus.Publish) 71 | .AddLift(PersonId, new Distance(10m, DistanceUnit.Kilometer)); 72 | 73 | journey.AddLift(PersonId, new Distance(10m, DistanceUnit.Kilometer)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Capabilities/Distance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Domain.Infrastructure.Markers; 3 | 4 | namespace Mors.Journeys.Domain.Journeys.Capabilities 5 | { 6 | [ValueObject] 7 | public struct Distance 8 | { 9 | private readonly decimal _amount; 10 | private readonly DistanceUnit _unit; 11 | 12 | public Distance(decimal amount, DistanceUnit unit) 13 | { 14 | if (amount < 0m) throw new ArgumentException(Messages.DistanceAmountMustNotBeNegative, "amount"); 15 | _amount = amount; 16 | _unit = unit; 17 | } 18 | 19 | public static implicit operator decimal(Distance distance) 20 | { 21 | return distance._amount; 22 | } 23 | 24 | public override bool Equals(object obj) 25 | { 26 | if (obj == null) return false; 27 | if (obj is Distance == false) return false; 28 | return Equals((Distance)obj); 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | return _amount.GetHashCode() * 37 ^ _unit.GetHashCode(); 34 | } 35 | 36 | public bool Equals(Distance other) 37 | { 38 | return _amount == other._amount; 39 | } 40 | 41 | public static bool operator==(Distance a, Distance b) 42 | { 43 | return a.Equals(b); 44 | } 45 | 46 | public static bool operator!=(Distance a, Distance b) 47 | { 48 | return !a.Equals(b); 49 | } 50 | 51 | public static bool operator<(Distance a, Distance b) 52 | { 53 | return a._amount < b._amount; 54 | } 55 | 56 | public static bool operator>(Distance a, Distance b) 57 | { 58 | return a._amount > b._amount; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Capabilities/DistanceUnit.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Journeys.Capabilities 2 | { 3 | public enum DistanceUnit 4 | { 5 | Kilometer 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Capabilities/Lift.cs: -------------------------------------------------------------------------------- 1 | using Mors.Journeys.Domain.Infrastructure.Markers; 2 | 3 | namespace Mors.Journeys.Domain.Journeys.Capabilities 4 | { 5 | [ValueObject] 6 | internal sealed class Lift 7 | { 8 | private readonly object _personId; 9 | 10 | public Lift(object personId) 11 | { 12 | _personId = personId; 13 | } 14 | 15 | public bool IsForPerson(object personId) 16 | { 17 | return _personId.Equals(personId); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Messages.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Domain.Journeys { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Messages { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Messages() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mors.Journeys.Domain.Journeys.Messages", typeof(Messages).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Cannot add lift with distance larger than journey distance.. 65 | /// 66 | public static string CannotAddLiftWithDistanceLargerThanJourneyDistance { 67 | get { 68 | return ResourceManager.GetString("CannotAddLiftWithDistanceLargerThanJourneyDistance", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Distance amount must not be negative.. 74 | /// 75 | public static string DistanceAmountMustNotBeNegative { 76 | get { 77 | return ResourceManager.GetString("DistanceAmountMustNotBeNegative", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Journey already contains lift for that person.. 83 | /// 84 | public static string JourneyAlreadyContainsLiftForThatPerson { 85 | get { 86 | return ResourceManager.GetString("JourneyAlreadyContainsLiftForThatPerson", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Mors.Journeys.Domain.Journeys.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 1.0.0 6 | Copyright © Łukasz Mrozek 2019 7 | Łukasz Mrozek 8 | Journeys domain for journeys application 9 | https://github.com/morsiu/Journeys 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Operations/Journey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Mors.Journeys.Data; 4 | using Mors.Journeys.Data.Events; 5 | using Mors.Journeys.Domain.Infrastructure.Collections; 6 | using Mors.Journeys.Domain.Infrastructure.Exceptions; 7 | using Mors.Journeys.Domain.Infrastructure.Markers; 8 | using Mors.Journeys.Domain.Journeys.Capabilities; 9 | 10 | namespace Mors.Journeys.Domain.Journeys.Operations 11 | { 12 | [Aggregate] 13 | public sealed class Journey : IHasId 14 | { 15 | private readonly object _id; 16 | private readonly DateTime _dateOfOccurrence; 17 | private readonly Distance _routeDistance; 18 | private readonly ImmutableList _lifts = ImmutableList.Empty; 19 | private readonly Action _eventPublisher; 20 | 21 | public Journey(object id, DateTime dateOfOccurrence, Distance routeDistance, Action eventPublisher) 22 | { 23 | _dateOfOccurrence = dateOfOccurrence; 24 | _eventPublisher = eventPublisher; 25 | _id = id; 26 | _routeDistance = routeDistance; 27 | _eventPublisher(new JourneyCreatedEvent(id, dateOfOccurrence, routeDistance)); 28 | } 29 | 30 | private Journey(Journey journey, ImmutableList lifts) 31 | { 32 | _dateOfOccurrence = journey._dateOfOccurrence; 33 | _eventPublisher = journey._eventPublisher; 34 | _id = journey._id; 35 | _lifts = lifts; 36 | _routeDistance = journey._routeDistance; 37 | } 38 | 39 | public Journey AddLift(object personId, Distance liftDistance) 40 | { 41 | if (ContainsLiftForPerson(personId)) 42 | throw new InvariantViolationException(Messages.JourneyAlreadyContainsLiftForThatPerson); 43 | if (liftDistance > _routeDistance) 44 | throw new InvariantViolationException(Messages.CannotAddLiftWithDistanceLargerThanJourneyDistance); 45 | var lift = new Lift(personId); 46 | var newLifts = _lifts.Add(lift); 47 | _eventPublisher(new LiftAddedEvent(_id, personId, liftDistance)); 48 | return new Journey(this, newLifts); 49 | } 50 | 51 | private bool ContainsLiftForPerson(object personId) 52 | { 53 | return _lifts.Any(lift => lift.IsForPerson(personId)); 54 | } 55 | 56 | object IHasId.Id 57 | { 58 | get { return _id; } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Journeys/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Mors.Journeys.Domain.Journeys.Test")] -------------------------------------------------------------------------------- /Mors.Journeys.Domain.People.Test/Mors.Journeys.Domain.People.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.People.Test/PersonTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Mors.Journeys.Data.Events; 3 | using Mors.Journeys.Domain.Infrastructure.Exceptions; 4 | using Mors.Journeys.Domain.Test; 5 | 6 | namespace Mors.Journeys.Domain.People.Test 7 | { 8 | [TestClass] 9 | public sealed class PersonTest 10 | { 11 | private static readonly string PersonName = "PersonName"; 12 | private static readonly object PersonId = new Id(0); 13 | private EventBus _eventBus; 14 | 15 | [TestInitialize] 16 | public void TestInitialize() 17 | { 18 | _eventBus = new EventBus(); 19 | } 20 | 21 | [TestMethod] 22 | public void ShouldPublishEventWhenCreatingPerson() 23 | { 24 | var eventMatcher = _eventBus.Listen(() => 25 | { 26 | new Person(PersonId, PersonName, _eventBus.Publish); 27 | }); 28 | 29 | eventMatcher.AssertReceivedOneEvent( 30 | evt => evt.PersonId.Equals(PersonId) && 31 | evt.PersonName == PersonName); 32 | } 33 | 34 | [TestMethod] 35 | [ExpectedException(typeof(InvariantViolationException))] 36 | public void ShouldReportInvariantViolationWhenCreatingPersonWithEmptyName() 37 | { 38 | new Person(PersonId, string.Empty, _eventBus.Publish); 39 | } 40 | 41 | [TestMethod] 42 | [ExpectedException(typeof(InvariantViolationException))] 43 | public void ShouldReportInvariantViolationWhenCreatingPersonWithNullName() 44 | { 45 | new Person(PersonId, null, _eventBus.Publish); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.People/Messages.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Mors.Journeys.Domain.People { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Messages { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Messages() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mors.Journeys.Domain.People.Messages", typeof(Messages).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Person must have a name.. 65 | /// 66 | public static string PersonMustHaveAName { 67 | get { 68 | return ResourceManager.GetString("PersonMustHaveAName", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.People/Mors.Journeys.Domain.People.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 1.0.0 6 | Copyright © Łukasz Mrozek 2019 7 | Łukasz Mrozek 8 | People domain for journeys application 9 | https://github.com/morsiu/Journeys 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.People/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mors.Journeys.Data; 3 | using Mors.Journeys.Data.Events; 4 | using Mors.Journeys.Domain.Infrastructure.Exceptions; 5 | using Mors.Journeys.Domain.Infrastructure.Markers; 6 | 7 | namespace Mors.Journeys.Domain.People 8 | { 9 | [Aggregate] 10 | public sealed class Person : IHasId 11 | { 12 | private readonly object _id; 13 | 14 | public Person(object id, string name, Action eventPublisher) 15 | { 16 | if (string.IsNullOrEmpty(name)) 17 | throw new InvariantViolationException(Messages.PersonMustHaveAName); 18 | _id = id; 19 | eventPublisher(new PersonCreatedEvent(id, name)); 20 | } 21 | 22 | object IHasId.Id 23 | { 24 | get { return _id; } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Test.Infrastructure/EventBusMock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Mors.Journeys.Domain.Infrastructure; 4 | 5 | namespace Mors.Journeys.Domain.Test 6 | { 7 | public sealed class EventBus 8 | { 9 | private readonly List _eventMatchers = new List(); 10 | 11 | public void Publish(object @event) 12 | { 13 | foreach (var eventMatcher in _eventMatchers) 14 | { 15 | eventMatcher.Add(@event); 16 | } 17 | } 18 | 19 | public EventMatcher Listen(Action action) 20 | { 21 | var matcher = new EventMatcher(); 22 | _eventMatchers.Add(matcher); 23 | action(); 24 | _eventMatchers.Remove(matcher); 25 | return matcher; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Test.Infrastructure/EventMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Mors.Journeys.Domain.Test 6 | { 7 | public sealed class EventMatcher 8 | { 9 | private readonly List _receivedEvents = new List(); 10 | 11 | public void Add(object @event) 12 | { 13 | _receivedEvents.Add(@event); 14 | } 15 | 16 | public void AssertReceivedOneEvent(Func eventMatcher) 17 | { 18 | Assert.AreEqual(1, _receivedEvents.Count, "Expected one event and got {0}.", _receivedEvents.Count); 19 | var receivedEventType = _receivedEvents[0].GetType(); 20 | Assert.AreEqual(typeof(TEvent), receivedEventType, "Expected event of type `{0}`, got `{1}`", typeof(TEvent), receivedEventType); 21 | var receivedEvent = (TEvent)_receivedEvents[0]; 22 | Assert.AreEqual(true, eventMatcher(receivedEvent), "Received event did not match criteria."); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Test.Infrastructure/Id.cs: -------------------------------------------------------------------------------- 1 | namespace Mors.Journeys.Domain.Test 2 | { 3 | public sealed class Id 4 | { 5 | private readonly int _id; 6 | 7 | public Id(int id) 8 | { 9 | _id = id; 10 | } 11 | 12 | public override bool Equals(object other) 13 | { 14 | return other != null 15 | && other is Id 16 | && Equals((Id)other); 17 | } 18 | 19 | public override int GetHashCode() 20 | { 21 | return _id.GetHashCode(); 22 | } 23 | 24 | private bool Equals(Id other) 25 | { 26 | return _id == other._id; 27 | } 28 | 29 | public static bool operator ==(Id a, Id b) 30 | { 31 | return a.Equals(b); 32 | } 33 | 34 | public static bool operator !=(Id a, Id b) 35 | { 36 | return !a.Equals(b); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Mors.Journeys.Domain.Test.Infrastructure/Mors.Journeys.Domain.Test.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | --------------------------------------------------------------------------------