diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Shipping.csproj b/src/Shipping/Shipping.csproj new file mode 100644 index 0000000..a8bbe1e --- /dev/null +++ b/src/Shipping/Shipping.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Shipping.csproj b/src/Shipping/Shipping.csproj new file mode 100644 index 0000000..a8bbe1e --- /dev/null +++ b/src/Shipping/Shipping.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shipping/ShippingDbContext.cs b/src/Shipping/ShippingDbContext.cs new file mode 100644 index 0000000..3fc778a --- /dev/null +++ b/src/Shipping/ShippingDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace Shipping +{ + public class ShippingDbContext : DbContext + { + public DbSet ShippingLabels { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Shipping"); + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(x => x.OrderId); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Shipping.csproj b/src/Shipping/Shipping.csproj new file mode 100644 index 0000000..a8bbe1e --- /dev/null +++ b/src/Shipping/Shipping.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shipping/ShippingDbContext.cs b/src/Shipping/ShippingDbContext.cs new file mode 100644 index 0000000..3fc778a --- /dev/null +++ b/src/Shipping/ShippingDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace Shipping +{ + public class ShippingDbContext : DbContext + { + public DbSet ShippingLabels { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Shipping"); + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(x => x.OrderId); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ShippingLabel.cs b/src/Shipping/ShippingLabel.cs new file mode 100644 index 0000000..dcb0be7 --- /dev/null +++ b/src/Shipping/ShippingLabel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Shipping +{ + public class ShippingLabel + { + [Key] + public Guid OrderId { get; set; } + public DateTime OrderDate { get; set; } + public bool Cancelled { get; set; } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Shipping.csproj b/src/Shipping/Shipping.csproj new file mode 100644 index 0000000..a8bbe1e --- /dev/null +++ b/src/Shipping/Shipping.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shipping/ShippingDbContext.cs b/src/Shipping/ShippingDbContext.cs new file mode 100644 index 0000000..3fc778a --- /dev/null +++ b/src/Shipping/ShippingDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace Shipping +{ + public class ShippingDbContext : DbContext + { + public DbSet ShippingLabels { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Shipping"); + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(x => x.OrderId); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ShippingLabel.cs b/src/Shipping/ShippingLabel.cs new file mode 100644 index 0000000..dcb0be7 --- /dev/null +++ b/src/Shipping/ShippingLabel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Shipping +{ + public class ShippingLabel + { + [Key] + public Guid OrderId { get; set; } + public DateTime OrderDate { get; set; } + public bool Cancelled { get; set; } + } +} \ No newline at end of file diff --git a/src/Worker/Program.cs b/src/Worker/Program.cs new file mode 100644 index 0000000..6e2a1ed --- /dev/null +++ b/src/Worker/Program.cs @@ -0,0 +1,39 @@ +using Billing; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Shipping; + +namespace Worker +{ + class Program + { + static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + + return endpointConfiguration; + }) + .ConfigureServices(services => + { + services.AddSales(); + services.AddShipping(); + }); + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46aef81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# JetBrains Rider +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..752a6d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Derek Comartin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderCancelled/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription new file mode 100644 index 0000000..20ae4e6 --- /dev/null +++ b/src/.learningtransport/.events/Sales.Contracts.OrderPlaced/LooselyCoupledMonolith.subscription @@ -0,0 +1 @@ +LooselyCoupledMonolith \ No newline at end of file diff --git a/src/AspNetCore/AspNetCore.csproj b/src/AspNetCore/AspNetCore.csproj new file mode 100644 index 0000000..d1bbf94 --- /dev/null +++ b/src/AspNetCore/AspNetCore.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/AspNetCore/Program.cs b/src/AspNetCore/Program.cs new file mode 100644 index 0000000..0d02003 --- /dev/null +++ b/src/AspNetCore/Program.cs @@ -0,0 +1,42 @@ +using Billing; +using Billing.Contracts; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Sales.Contracts; +using Shipping; +using Shipping.Contracts; + +namespace AspNetCore +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + endpointConfiguration.SendOnly(); + + return endpointConfiguration; + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AspNetCore/Properties/launchSettings.json b/src/AspNetCore/Properties/launchSettings.json new file mode 100644 index 0000000..ee51616 --- /dev/null +++ b/src/AspNetCore/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64790", + "sslPort": 44374 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AspNetCore/Startup.cs b/src/AspNetCore/Startup.cs new file mode 100644 index 0000000..c95df24 --- /dev/null +++ b/src/AspNetCore/Startup.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Sales; +using Shipping; + +namespace AspNetCore +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSales(); + services.AddShipping(); + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapSales(); + endpoints.MapShipping(); + }); + } + } +} diff --git a/src/AspNetCore/appsettings.Development.json b/src/AspNetCore/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/AspNetCore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AspNetCore/appsettings.json b/src/AspNetCore/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/src/AspNetCore/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Billing.Contracts/BillOrder.cs b/src/Billing.Contracts/BillOrder.cs new file mode 100644 index 0000000..c504a2d --- /dev/null +++ b/src/Billing.Contracts/BillOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class BillOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/Billing.Contracts.csproj b/src/Billing.Contracts/Billing.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Billing.Contracts/Billing.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Billing.Contracts/OrderBilled.cs b/src/Billing.Contracts/OrderBilled.cs new file mode 100644 index 0000000..e3a7bb8 --- /dev/null +++ b/src/Billing.Contracts/OrderBilled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderBilled : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/OrderRefunded.cs b/src/Billing.Contracts/OrderRefunded.cs new file mode 100644 index 0000000..27ccf0b --- /dev/null +++ b/src/Billing.Contracts/OrderRefunded.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class OrderRefunded : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing.Contracts/RefundOrder.cs b/src/Billing.Contracts/RefundOrder.cs new file mode 100644 index 0000000..9a7fac2 --- /dev/null +++ b/src/Billing.Contracts/RefundOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Billing.Contracts +{ + public class RefundOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Billing/BillOrder.cs b/src/Billing/BillOrder.cs new file mode 100644 index 0000000..3eb306e --- /dev/null +++ b/src/Billing/BillOrder.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; + +namespace Billing +{ + public class BillOrderHandler : IHandleMessages + { + public async Task Handle(BillOrder message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new OrderBilled + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj new file mode 100644 index 0000000..0e5a0a5 --- /dev/null +++ b/src/Billing/Billing.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Billing/Endpoints.cs b/src/Billing/Endpoints.cs new file mode 100644 index 0000000..a6609e2 --- /dev/null +++ b/src/Billing/Endpoints.cs @@ -0,0 +1,15 @@ +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public static class Endpoints + { + public static void MapBilling(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderBilled).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Billing/RefundOrderHandler.cs b/src/Billing/RefundOrderHandler.cs new file mode 100644 index 0000000..0e4afb4 --- /dev/null +++ b/src/Billing/RefundOrderHandler.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; + +namespace Billing +{ + public class RefundOrderHandler : IHandleMessages + { + public async Task Handle(RefundOrder message, IMessageHandlerContext context) + { + await context.Publish(refunded => + { + refunded.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/LooselyCoupledMonolith.sln b/src/LooselyCoupledMonolith.sln new file mode 100644 index 0000000..de45722 --- /dev/null +++ b/src/LooselyCoupledMonolith.sln @@ -0,0 +1,176 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales", "Sales\Sales.csproj", "{018A96A1-C587-43C6-BB9C-12AB654F6CC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Contracts", "Sales.Contracts\Sales.Contracts.csproj", "{FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sales.Tests", "Sales.Tests\Sales.Tests.csproj", "{FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{18E2D615-2585-48C5-82A5-93CC798C54B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Contracts", "Shipping.Contracts\Shipping.Contracts.csproj", "{BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping.Tests", "Shipping.Tests\Shipping.Tests.csproj", "{F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sales", "Sales", "{7D57F573-B99C-45B1-A5C3-5710F7E4083B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shipping", "Shipping", "{197792D6-E8EE-43E3-9330-6A15A518487E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "Worker\Worker.csproj", "{D1542A48-599D-46EA-A35D-24FE47649416}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore", "AspNetCore\AspNetCore.csproj", "{4986376E-4D3C-4751-99FE-176F61A0BAA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing", "Billing\Billing.csproj", "{B179C1F1-86CB-4796-AE31-95EB314BA094}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Billing", "Billing", "{3A2E2769-EAF7-473B-9F32-2578709B911D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Contracts", "Billing.Contracts\Billing.Contracts.csproj", "{77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x64.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Debug|x86.Build.0 = Debug|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x64.Build.0 = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.ActiveCfg = Release|Any CPU + {018A96A1-C587-43C6-BB9C-12AB654F6CC6}.Release|x86.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x64.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Debug|x86.Build.0 = Debug|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|Any CPU.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x64.Build.0 = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.ActiveCfg = Release|Any CPU + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D}.Release|x86.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x64.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Debug|x86.Build.0 = Debug|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|Any CPU.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x64.Build.0 = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.ActiveCfg = Release|Any CPU + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975}.Release|x86.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x64.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Debug|x86.Build.0 = Debug|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|Any CPU.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x64.Build.0 = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.ActiveCfg = Release|Any CPU + {18E2D615-2585-48C5-82A5-93CC798C54B8}.Release|x86.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x64.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Debug|x86.Build.0 = Debug|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x64.Build.0 = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.ActiveCfg = Release|Any CPU + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E}.Release|x86.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x64.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Debug|x86.Build.0 = Debug|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x64.Build.0 = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.ActiveCfg = Release|Any CPU + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1}.Release|x86.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x64.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Debug|x86.Build.0 = Debug|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|Any CPU.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x64.Build.0 = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.ActiveCfg = Release|Any CPU + {D1542A48-599D-46EA-A35D-24FE47649416}.Release|x86.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x64.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Debug|x86.Build.0 = Debug|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|Any CPU.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x64.Build.0 = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.ActiveCfg = Release|Any CPU + {4986376E-4D3C-4751-99FE-176F61A0BAA2}.Release|x86.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x64.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.ActiveCfg = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Debug|x86.Build.0 = Debug|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|Any CPU.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x64.Build.0 = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.ActiveCfg = Release|Any CPU + {B179C1F1-86CB-4796-AE31-95EB314BA094}.Release|x86.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x64.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Debug|x86.Build.0 = Debug|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|Any CPU.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x64.Build.0 = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.ActiveCfg = Release|Any CPU + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {018A96A1-C587-43C6-BB9C-12AB654F6CC6} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FBC583E2-8BA8-4DD2-B9D4-BC76D13C347D} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {FDCEB20D-64A6-426E-9E5B-9FB8AC88B975} = {7D57F573-B99C-45B1-A5C3-5710F7E4083B} + {18E2D615-2585-48C5-82A5-93CC798C54B8} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {BAA655B9-BE4B-40E5-9A19-D8BEDFBDA31E} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {F61CB79C-4B4B-4D89-8047-C91C9EA7AEE1} = {197792D6-E8EE-43E3-9330-6A15A518487E} + {B179C1F1-86CB-4796-AE31-95EB314BA094} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + {77DCE1A7-64F0-49D8-9AC0-7DE397EF81D0} = {3A2E2769-EAF7-473B-9F32-2578709B911D} + EndGlobalSection +EndGlobal diff --git a/src/Sales.Contracts/CancelOrder.cs b/src/Sales.Contracts/CancelOrder.cs new file mode 100644 index 0000000..f264adb --- /dev/null +++ b/src/Sales.Contracts/CancelOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderCancelled.cs b/src/Sales.Contracts/OrderCancelled.cs new file mode 100644 index 0000000..d790bf8 --- /dev/null +++ b/src/Sales.Contracts/OrderCancelled.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/OrderPlaced.cs b/src/Sales.Contracts/OrderPlaced.cs new file mode 100644 index 0000000..f99287b --- /dev/null +++ b/src/Sales.Contracts/OrderPlaced.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class OrderPlaced : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/PlaceOrder.cs b/src/Sales.Contracts/PlaceOrder.cs new file mode 100644 index 0000000..20ca626 --- /dev/null +++ b/src/Sales.Contracts/PlaceOrder.cs @@ -0,0 +1,7 @@ +using System; +using NServiceBus; + +public class PlaceOrder : ICommand +{ + public Guid OrderId { get; set; } +} \ No newline at end of file diff --git a/src/Sales.Contracts/ReadyToShipOrder.cs b/src/Sales.Contracts/ReadyToShipOrder.cs new file mode 100644 index 0000000..e6fa0f6 --- /dev/null +++ b/src/Sales.Contracts/ReadyToShipOrder.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Sales.Contracts +{ + public class ReadyToShipOrder : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales.Contracts/Sales.Contracts.csproj b/src/Sales.Contracts/Sales.Contracts.csproj new file mode 100644 index 0000000..7f31933 --- /dev/null +++ b/src/Sales.Contracts/Sales.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Sales.Tests/ConfigureServicesTests.cs b/src/Sales.Tests/ConfigureServicesTests.cs new file mode 100644 index 0000000..73c3238 --- /dev/null +++ b/src/Sales.Tests/ConfigureServicesTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Sales.Tests +{ + public class ConfigureServicesTests + { + [Fact] + public void DbContext_should_resolve() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSales(); + + var provider = serviceCollection.BuildServiceProvider(); + var dbContext = provider.GetService(); + dbContext.ShouldNotBeNull(); + } + } +} diff --git a/src/Sales.Tests/Sales.Tests.csproj b/src/Sales.Tests/Sales.Tests.csproj new file mode 100644 index 0000000..b9b3995 --- /dev/null +++ b/src/Sales.Tests/Sales.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/Sales/CancelOrderHandler.cs b/src/Sales/CancelOrderHandler.cs new file mode 100644 index 0000000..1c86008 --- /dev/null +++ b/src/Sales/CancelOrderHandler.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public class CancelOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public CancelOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(CancelOrder message, IMessageHandlerContext context) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == message.OrderId); + order.Status = OrderStatus.Cancelled; + await _dbContext.SaveChangesAsync(); + + await context.Publish(cancelled => + { + cancelled.OrderId = message.OrderId; + }); + } + } +} \ No newline at end of file diff --git a/src/Sales/ConfigureServices.cs b/src/Sales/ConfigureServices.cs new file mode 100644 index 0000000..7099c68 --- /dev/null +++ b/src/Sales/ConfigureServices.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Sales +{ + public static class ConfigureServices + { + public static void AddSales(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + } + } +} diff --git a/src/Sales/Endpoints.cs b/src/Sales/Endpoints.cs new file mode 100644 index 0000000..857812f --- /dev/null +++ b/src/Sales/Endpoints.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Sales.Contracts; + +namespace Sales +{ + public static class Endpoints + { + public static void MapSales(this IEndpointRouteBuilder endpoints) + { + + } + + public static void MapSales(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(OrderPlaced).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Sales/Extensions.cs b/src/Sales/Extensions.cs new file mode 100644 index 0000000..f940e11 --- /dev/null +++ b/src/Sales/Extensions.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Sales +{ + public static class Extensions + { + public static async Task Deserialize(this HttpResponseMessage responseMessage) + { + return JsonConvert.DeserializeObject(await responseMessage.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/src/Sales/ExternalOrderIntegration.cs b/src/Sales/ExternalOrderIntegration.cs new file mode 100644 index 0000000..a231126 --- /dev/null +++ b/src/Sales/ExternalOrderIntegration.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using NServiceBus; + +namespace Sales +{ + public class ExternalOrderIntegration + { + private readonly IMessageSession _messageSession; + private readonly HttpClient _httpClient; + + public ExternalOrderIntegration(IMessageSession messageSession, HttpClient httpClient) + { + _messageSession = messageSession; + _httpClient = httpClient; + } + + public async Task PlaceOrder() + { + var response = await _httpClient.GetAsync("http://external.com/newOrders"); + var externalOrder = await response.Deserialize(); + + var placeOrder = new PlaceOrder + { + OrderId = externalOrder!.Id, + // Translation to convert an external order or PlaceOrder command + }; + await _messageSession.Send(placeOrder); + } + } + + public class ExternalOrder + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Sales/Order.cs b/src/Sales/Order.cs new file mode 100644 index 0000000..49417f4 --- /dev/null +++ b/src/Sales/Order.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Sales +{ + [Table("Orders")] + public class Order + { + [Key] + public Guid OrderId { get; set; } + + public OrderStatus Status { get; set; } + } + + public enum OrderStatus + { + Pending = 0, + ReadyToShip = 1, + Shipped = 2, + Cancelled = 3 + } +} \ No newline at end of file diff --git a/src/Sales/PlaceOrderHandler.cs b/src/Sales/PlaceOrderHandler.cs new file mode 100644 index 0000000..57bbf53 --- /dev/null +++ b/src/Sales/PlaceOrderHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Billing.Contracts; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class PlaceOrderController : Controller + { + private readonly IMessageSession _messageSession; + + public PlaceOrderController(IMessageSession messageSession) + { + _messageSession = messageSession; + } + + [HttpPost("/sales/orders/{orderId:Guid}")] + public async Task Action([FromRoute] Guid orderId) + { + await _messageSession.Send(new PlaceOrder + { + OrderId = orderId + }); + + return NoContent(); + } + } + + public class PlaceOrderHandler : IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public PlaceOrderHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(PlaceOrder message, IMessageHandlerContext context) + { + await _dbContext.Orders.AddAsync(new Order + { + OrderId = message.OrderId, + Status = OrderStatus.Pending + }); + await _dbContext.SaveChangesAsync(); + + var orderPlaced = new OrderPlaced + { + OrderId = message.OrderId + }; + await context.Publish(orderPlaced); + } + } + + public class PlaceOrderSagaData : ContainSagaData + { + public Guid OrderId { get; set; } + } + + public class PlaceOrderSaga : + Saga, + IAmStartedByMessages, + IHandleMessages, + IHandleMessages + { + + protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) + { + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + mapper.ConfigureMapping(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId); + } + + public async Task Handle(OrderPlaced message, IMessageHandlerContext context) + { + await context.Send(new BillOrder + { + OrderId = message.OrderId + }); + } + + public async Task Handle(OrderBilled message, IMessageHandlerContext context) + { + await context.Send(new CreateShippingLabel + { + OrderId = message.OrderId + }); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await context.Send(new ReadyToShipOrder + { + OrderId = Data.OrderId + }); + + MarkAsComplete(); + } + + } +} \ No newline at end of file diff --git a/src/Sales/ReadyToShipHandler.cs b/src/Sales/ReadyToShipHandler.cs new file mode 100644 index 0000000..2b6541c --- /dev/null +++ b/src/Sales/ReadyToShipHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Sales +{ + public class ReadyToShipHandler : + IHandleMessages, + IHandleMessages + { + private readonly SalesDbContext _dbContext; + + public ReadyToShipHandler(SalesDbContext dbContext) + { + _dbContext = dbContext; + } + + private async Task SetStatus(Guid orderId) + { + var order = await _dbContext.Orders.SingleAsync(x => x.OrderId == orderId); + order.Status = OrderStatus.ReadyToShip; + await _dbContext.SaveChangesAsync(); + } + + public async Task Handle(ShippingLabelCreated message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + + public async Task Handle(ReadyToShipOrder message, IMessageHandlerContext context) + { + await SetStatus(message.OrderId); + } + } +} \ No newline at end of file diff --git a/src/Sales/Sales.csproj b/src/Sales/Sales.csproj new file mode 100644 index 0000000..5992e86 --- /dev/null +++ b/src/Sales/Sales.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sales/SalesDbContext.cs b/src/Sales/SalesDbContext.cs new file mode 100644 index 0000000..42cbb1c --- /dev/null +++ b/src/Sales/SalesDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sales +{ + public class SalesDbContext : DbContext + { + public DbSet Orders { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Sales"); + base.OnConfiguring(optionsBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Backordered.cs b/src/Shipping.Contracts/Backordered.cs new file mode 100644 index 0000000..da021b3 --- /dev/null +++ b/src/Shipping.Contracts/Backordered.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class Backordered : IEvent + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/CreateShippingLabel.cs b/src/Shipping.Contracts/CreateShippingLabel.cs new file mode 100644 index 0000000..a65a2a6 --- /dev/null +++ b/src/Shipping.Contracts/CreateShippingLabel.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class CreateShippingLabel : ICommand + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Contracts/Shipping.Contracts.csproj b/src/Shipping.Contracts/Shipping.Contracts.csproj new file mode 100644 index 0000000..24ff1bb --- /dev/null +++ b/src/Shipping.Contracts/Shipping.Contracts.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Shipping.Contracts/ShippingLabelCreated.cs b/src/Shipping.Contracts/ShippingLabelCreated.cs new file mode 100644 index 0000000..125f0ac --- /dev/null +++ b/src/Shipping.Contracts/ShippingLabelCreated.cs @@ -0,0 +1,10 @@ +using System; +using NServiceBus; + +namespace Shipping.Contracts +{ + public class ShippingLabelCreated : IMessage + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/src/Shipping.Tests/Shipping.Tests.csproj b/src/Shipping.Tests/Shipping.Tests.csproj new file mode 100644 index 0000000..5b1791f --- /dev/null +++ b/src/Shipping.Tests/Shipping.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/Shipping.Tests/UnitTest1.cs b/src/Shipping.Tests/UnitTest1.cs new file mode 100644 index 0000000..f481c69 --- /dev/null +++ b/src/Shipping.Tests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Shipping.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Shipping/CancelShippingLabelHandler.cs b/src/Shipping/CancelShippingLabelHandler.cs new file mode 100644 index 0000000..b4e03a9 --- /dev/null +++ b/src/Shipping/CancelShippingLabelHandler.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NServiceBus; +using Sales.Contracts; + +namespace Shipping +{ + public class CancelShippingLabelHandler : IHandleMessages + { + private readonly ShippingDbContext _dbContext; + + public CancelShippingLabelHandler(ShippingDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(OrderCancelled message, IMessageHandlerContext context) + { + var order = await _dbContext.ShippingLabels.SingleAsync(x => x.OrderId == message.OrderId); + order.Cancelled = true; + await _dbContext.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ConfigureServices.cs b/src/Shipping/ConfigureServices.cs new file mode 100644 index 0000000..429c1d3 --- /dev/null +++ b/src/Shipping/ConfigureServices.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Shipping +{ + public static class ConfigureServices + { + public static void AddShipping(this IServiceCollection serviceCollection) + { + serviceCollection.AddDbContext(); + serviceCollection.AddTransient(); + } + } +} diff --git a/src/Shipping/CreateShippingLabelHandler.cs b/src/Shipping/CreateShippingLabelHandler.cs new file mode 100644 index 0000000..c302368 --- /dev/null +++ b/src/Shipping/CreateShippingLabelHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Billing.Contracts; +using NServiceBus; +using Sales.Contracts; +using Shipping.Contracts; + +namespace Shipping +{ + public class CreateShippingLabelHandler : IHandleMessages + { + public async Task Handle(CreateShippingLabel message, IMessageHandlerContext context) + { + // Do some work + + // Reply + await context.Reply(new ShippingLabelCreated + { + OrderId = message.OrderId + }); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Endpoints.cs b/src/Shipping/Endpoints.cs new file mode 100644 index 0000000..720eb39 --- /dev/null +++ b/src/Shipping/Endpoints.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using NServiceBus; +using Shipping.Contracts; + +namespace Shipping +{ + public static class Endpoints + { + public static void MapShipping(this IEndpointRouteBuilder endpoints) + { + endpoints.MapGet("/shipping", async context => + { + await context.Response.WriteAsync("Hello Shipping!"); + }); + } + + public static void MapShipping(this RoutingSettings routing) + { + routing.RouteToEndpoint( + assembly: typeof(ShippingLabelCreated).Assembly, + destination: "LooselyCoupledMonolith"); + } + } +} \ No newline at end of file diff --git a/src/Shipping/Shipping.csproj b/src/Shipping/Shipping.csproj new file mode 100644 index 0000000..a8bbe1e --- /dev/null +++ b/src/Shipping/Shipping.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shipping/ShippingDbContext.cs b/src/Shipping/ShippingDbContext.cs new file mode 100644 index 0000000..3fc778a --- /dev/null +++ b/src/Shipping/ShippingDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace Shipping +{ + public class ShippingDbContext : DbContext + { + public DbSet ShippingLabels { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("LooselyCoupledMonolith_Shipping"); + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(x => x.OrderId); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/src/Shipping/ShippingLabel.cs b/src/Shipping/ShippingLabel.cs new file mode 100644 index 0000000..dcb0be7 --- /dev/null +++ b/src/Shipping/ShippingLabel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Shipping +{ + public class ShippingLabel + { + [Key] + public Guid OrderId { get; set; } + public DateTime OrderDate { get; set; } + public bool Cancelled { get; set; } + } +} \ No newline at end of file diff --git a/src/Worker/Program.cs b/src/Worker/Program.cs new file mode 100644 index 0000000..6e2a1ed --- /dev/null +++ b/src/Worker/Program.cs @@ -0,0 +1,39 @@ +using Billing; +using Microsoft.Extensions.Hosting; +using NServiceBus; +using Sales; +using Shipping; + +namespace Worker +{ + class Program + { + static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNServiceBus(context => + { + var endpointConfiguration = new EndpointConfiguration("LooselyCoupledMonolith"); + var transport = endpointConfiguration.UseTransport(); + var persistence = endpointConfiguration.UsePersistence(); + + var routing = transport.Routing(); + + routing.MapBilling(); + routing.MapSales(); + routing.MapShipping(); + + + return endpointConfiguration; + }) + .ConfigureServices(services => + { + services.AddSales(); + services.AddShipping(); + }); + } +} diff --git a/src/Worker/Worker.csproj b/src/Worker/Worker.csproj new file mode 100644 index 0000000..fd0d7c2 --- /dev/null +++ b/src/Worker/Worker.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + +