diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1801ec6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,32 @@ +[*.cs] + +# CS8618: When exiting the constructor, the non-nullable field must contain a non-null value. Please consider declaring as nullable。 +dotnet_diagnostic.CS8618.severity = none + +# top-most EditorConfig file +root = true + +# Don't use tabs for indentation. +[*] +indent_style = space +trim_trailing_whitespace = true +guidelines = 140 +max_line_length = 140 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8 + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct,xml,stylecop}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18a2b91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,222 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +.vs/ +# Visual Studio 2015 cache/options directory +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +# NUNIT +*.VisualState.xml +TestResult.xml +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc +*.exe +*.bak +# Chutzpah Test files +_Chutzpah* +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.cache +*.VC.db +*.VC.VC.opendb +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap +# TFS 2012 Local Workspace +$tf/ +# Guidance Automation Toolkit +*.gpState +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user +# JustCode is a .NET coding add-in +.JustCode +# TeamCity is a build add-in +_TeamCity* +# DotCover is a Code Coverage Tool +*.dotCover +# 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 +# TODO: 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 +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable 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 +# 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 +node_modules/ +orleans.codegen.cs +# 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 +# SQL Server files +*.mdf +*.ldf +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +# Microsoft Fakes +FakesAssemblies/ +# GhostDoc plugin setting file +*.GhostDoc.xml +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +# Visual Studio 6 build log +*.plg +# Visual Studio 6 workspace options file +*.opt +# 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/ +# JetBrains Rider +.idea/ +*.sln.iml +# CodeRush +.cr/ +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc +package-lock.json +# 忽略windows生成的缩略图文件 +Thunbs.db +#忽略nuget库 +packages/ +.vscode/ \ No newline at end of file diff --git a/.vs/MASA.BuildingBlocks/DesignTimeBuild/.dtbcache.v2 b/.vs/MASA.BuildingBlocks/DesignTimeBuild/.dtbcache.v2 deleted file mode 100644 index 56f0e17..0000000 Binary files a/.vs/MASA.BuildingBlocks/DesignTimeBuild/.dtbcache.v2 and /dev/null differ diff --git a/.vs/MASA.BuildingBlocks/v17/.futdcache.v1 b/.vs/MASA.BuildingBlocks/v17/.futdcache.v1 deleted file mode 100644 index 06455aa..0000000 Binary files a/.vs/MASA.BuildingBlocks/v17/.futdcache.v1 and /dev/null differ diff --git a/.vs/MASA.BuildingBlocks/v17/.suo b/.vs/MASA.BuildingBlocks/v17/.suo deleted file mode 100644 index 29d4cd3..0000000 Binary files a/.vs/MASA.BuildingBlocks/v17/.suo and /dev/null differ diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..5e44171 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,24 @@ + + + $(AssemblyName) + packageIcon.png + masastack + © masastack Corporation. All rights reserved. + packageIcon.png + https://github.com/masastack/Masa.BuildingBlocks + git + true + $(MSBuildThisFileDirectory) + LICENSE.txt + + + + True + + + + True + + + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3f96be7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) MASA Stack + +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. \ No newline at end of file diff --git a/Masa.BuildingBlocks.sln b/Masa.BuildingBlocks.sln new file mode 100644 index 0000000..c198518 --- /dev/null +++ b/Masa.BuildingBlocks.sln @@ -0,0 +1,150 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31521.260 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BasicAbility", "BasicAbility", "{5DFAF4A2-ECB5-46E4-904D-1EA5F48B2D48}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dispatcher", "Dispatcher", "{FBD326D3-E59C-433E-A88E-14E179E3093D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "I18n", "I18n", "{EA2668AF-28E3-42C5-9FA5-8C9FF377180E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Isolation", "Isolation", "{022D6FF5-4B65-4213-9A97-C69E2B2F99E1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Observability", "Observability", "{75050CBC-A0F2-408A-A582-54EF37450B29}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReadWriteSpliting", "ReadWriteSpliting", "{509BDB5A-5D32-478F-BF27-F0470C18C7C9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SearchEngine", "SearchEngine", "{8C39C640-0E8A-43A7-890C-9742B6B70AA4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Service", "Service", "{593A3114-D1E0-47ED-BC37-58E08886175B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testable", "Testable", "{57AD498B-D9AF-4479-8C14-45507F6509F5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DDD", "DDD", "{9CB643CA-AA09-46E7-8CB9-E1D55E84B32B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{64FB8703-E922-45DE-9D01-3FE9EFE56727}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{77D17E30-CB7C-4DD7-8CF1-9D5350FF2304}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6ED365E6-4A1A-499F-85FB-F22E865CA4BA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "design", "design", "{43157B67-5CDB-4D28-9095-9586C685CA6B}" + ProjectSection(SolutionItems) = preProject + design\CodeMap.dgml = design\CodeMap.dgml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{884D1A0A-A24F-4ED9-BBFE-DCC4E47CF26D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Service.MinimalAPIs", "src\Service\Masa.BuildingBlocks.Service.MinimalAPIs\Masa.BuildingBlocks.Service.MinimalAPIs.csproj", "{F128E74B-CEC4-4915-AEE2-FF69E60D17F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.SearchEngine.AutoComplete", "src\SearchEngine\Masa.BuildingBlocks.SearchEngine.AutoComplete\Masa.BuildingBlocks.SearchEngine.AutoComplete.csproj", "{9782B9B5-22D0-432A-9BC5-D830C9478D38}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.ReadWriteSpliting.CQRS", "src\ReadWriteSpliting\Masa.BuildingBlocks.ReadWriteSpliting.CQRS\Masa.BuildingBlocks.ReadWriteSpliting.CQRS.csproj", "{8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing", "src\ReadWriteSpliting\Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing\Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing.csproj", "{6175D1D5-8007-4951-BF12-0064B8201C35}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Dispatcher.Events", "src\Dispatcher\Masa.BuildingBlocks.Dispatcher.Events\Masa.BuildingBlocks.Dispatcher.Events.csproj", "{EECA2E9C-36B4-4DCE-93F7-487955B6260E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Dispatcher.IntegrationEvents", "src\Dispatcher\Masa.BuildingBlocks.Dispatcher.IntegrationEvents\Masa.BuildingBlocks.Dispatcher.IntegrationEvents.csproj", "{4A2ECF39-8999-4C18-83D4-6587A1D9D9EE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.DDD.Domain", "src\DDD\Masa.BuildingBlocks.DDD.Domain\Masa.BuildingBlocks.DDD.Domain.csproj", "{D672874A-B848-4561-8D0C-11B58B7F308B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Data.Contracts", "src\Data\Masa.BuildingBlocks.Data.Contracts\Masa.BuildingBlocks.Data.Contracts.csproj", "{B735824E-D918-4140-AE94-CA4172BF59EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Data.UoW", "src\Data\Masa.BuildingBlocks.Data.UoW\Masa.BuildingBlocks.Data.UoW.csproj", "{41F94385-3EAC-4617-8C2B-74D606B0A6A6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Configuration", "src\Configuration\Masa.BuildingBlocks.Configuration\Masa.BuildingBlocks.Configuration.csproj", "{E86483C1-1479-453A-8ABC-35DAC95AC056}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Configuration.Tests", "test\Masa.BuildingBlocks.Configuration.Tests\Masa.BuildingBlocks.Configuration.Tests.csproj", "{E7F59F98-3D2A-46BC-83FC-CB66A8D1041C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.DDD.Domain.Tests", "test\Masa.BuildingBlocks.DDD.Domain.Tests\Masa.BuildingBlocks.DDD.Domain.Tests.csproj", "{5791FF31-6579-46D6-8E65-48031C947B7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F128E74B-CEC4-4915-AEE2-FF69E60D17F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F128E74B-CEC4-4915-AEE2-FF69E60D17F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F128E74B-CEC4-4915-AEE2-FF69E60D17F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F128E74B-CEC4-4915-AEE2-FF69E60D17F0}.Release|Any CPU.Build.0 = Release|Any CPU + {9782B9B5-22D0-432A-9BC5-D830C9478D38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9782B9B5-22D0-432A-9BC5-D830C9478D38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9782B9B5-22D0-432A-9BC5-D830C9478D38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9782B9B5-22D0-432A-9BC5-D830C9478D38}.Release|Any CPU.Build.0 = Release|Any CPU + {8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C}.Release|Any CPU.Build.0 = Release|Any CPU + {6175D1D5-8007-4951-BF12-0064B8201C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6175D1D5-8007-4951-BF12-0064B8201C35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6175D1D5-8007-4951-BF12-0064B8201C35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6175D1D5-8007-4951-BF12-0064B8201C35}.Release|Any CPU.Build.0 = Release|Any CPU + {EECA2E9C-36B4-4DCE-93F7-487955B6260E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EECA2E9C-36B4-4DCE-93F7-487955B6260E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EECA2E9C-36B4-4DCE-93F7-487955B6260E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EECA2E9C-36B4-4DCE-93F7-487955B6260E}.Release|Any CPU.Build.0 = Release|Any CPU + {4A2ECF39-8999-4C18-83D4-6587A1D9D9EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A2ECF39-8999-4C18-83D4-6587A1D9D9EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A2ECF39-8999-4C18-83D4-6587A1D9D9EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A2ECF39-8999-4C18-83D4-6587A1D9D9EE}.Release|Any CPU.Build.0 = Release|Any CPU + {D672874A-B848-4561-8D0C-11B58B7F308B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D672874A-B848-4561-8D0C-11B58B7F308B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D672874A-B848-4561-8D0C-11B58B7F308B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D672874A-B848-4561-8D0C-11B58B7F308B}.Release|Any CPU.Build.0 = Release|Any CPU + {B735824E-D918-4140-AE94-CA4172BF59EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B735824E-D918-4140-AE94-CA4172BF59EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B735824E-D918-4140-AE94-CA4172BF59EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B735824E-D918-4140-AE94-CA4172BF59EB}.Release|Any CPU.Build.0 = Release|Any CPU + {41F94385-3EAC-4617-8C2B-74D606B0A6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41F94385-3EAC-4617-8C2B-74D606B0A6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41F94385-3EAC-4617-8C2B-74D606B0A6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41F94385-3EAC-4617-8C2B-74D606B0A6A6}.Release|Any CPU.Build.0 = Release|Any CPU + {E86483C1-1479-453A-8ABC-35DAC95AC056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E86483C1-1479-453A-8ABC-35DAC95AC056}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E86483C1-1479-453A-8ABC-35DAC95AC056}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E86483C1-1479-453A-8ABC-35DAC95AC056}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F59F98-3D2A-46BC-83FC-CB66A8D1041C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F59F98-3D2A-46BC-83FC-CB66A8D1041C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F59F98-3D2A-46BC-83FC-CB66A8D1041C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F59F98-3D2A-46BC-83FC-CB66A8D1041C}.Release|Any CPU.Build.0 = Release|Any CPU + {5791FF31-6579-46D6-8E65-48031C947B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5791FF31-6579-46D6-8E65-48031C947B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5791FF31-6579-46D6-8E65-48031C947B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5791FF31-6579-46D6-8E65-48031C947B7D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5DFAF4A2-ECB5-46E4-904D-1EA5F48B2D48} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {FBD326D3-E59C-433E-A88E-14E179E3093D} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {EA2668AF-28E3-42C5-9FA5-8C9FF377180E} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {022D6FF5-4B65-4213-9A97-C69E2B2F99E1} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {75050CBC-A0F2-408A-A582-54EF37450B29} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {509BDB5A-5D32-478F-BF27-F0470C18C7C9} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {8C39C640-0E8A-43A7-890C-9742B6B70AA4} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {593A3114-D1E0-47ED-BC37-58E08886175B} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {57AD498B-D9AF-4479-8C14-45507F6509F5} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {9CB643CA-AA09-46E7-8CB9-E1D55E84B32B} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {64FB8703-E922-45DE-9D01-3FE9EFE56727} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {884D1A0A-A24F-4ED9-BBFE-DCC4E47CF26D} = {77D17E30-CB7C-4DD7-8CF1-9D5350FF2304} + {F128E74B-CEC4-4915-AEE2-FF69E60D17F0} = {593A3114-D1E0-47ED-BC37-58E08886175B} + {9782B9B5-22D0-432A-9BC5-D830C9478D38} = {8C39C640-0E8A-43A7-890C-9742B6B70AA4} + {8DAC72C3-FB8B-4F9B-AC44-F98CC665CB0C} = {509BDB5A-5D32-478F-BF27-F0470C18C7C9} + {6175D1D5-8007-4951-BF12-0064B8201C35} = {509BDB5A-5D32-478F-BF27-F0470C18C7C9} + {EECA2E9C-36B4-4DCE-93F7-487955B6260E} = {FBD326D3-E59C-433E-A88E-14E179E3093D} + {4A2ECF39-8999-4C18-83D4-6587A1D9D9EE} = {FBD326D3-E59C-433E-A88E-14E179E3093D} + {D672874A-B848-4561-8D0C-11B58B7F308B} = {9CB643CA-AA09-46E7-8CB9-E1D55E84B32B} + {B735824E-D918-4140-AE94-CA4172BF59EB} = {64FB8703-E922-45DE-9D01-3FE9EFE56727} + {41F94385-3EAC-4617-8C2B-74D606B0A6A6} = {64FB8703-E922-45DE-9D01-3FE9EFE56727} + {E86483C1-1479-453A-8ABC-35DAC95AC056} = {884D1A0A-A24F-4ED9-BBFE-DCC4E47CF26D} + {E7F59F98-3D2A-46BC-83FC-CB66A8D1041C} = {6ED365E6-4A1A-499F-85FB-F22E865CA4BA} + {5791FF31-6579-46D6-8E65-48031C947B7D} = {6ED365E6-4A1A-499F-85FB-F22E865CA4BA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {40383055-CC50-4600-AD9A-53C14F620D03} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb23998 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +[中](README.zh-CN.md) | EN +# Masa.BuildingBlocks +Building blocks of the MASA Stack, provides a unified interface standard for MASA Contrib's implementation specifications and process connector. diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..f2f7631 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,3 @@ +中 | [EN](README.md) +# Masa.BuildingBlocks +MASA Stack的构建块,提供统一的接口标准,用于MASA Contrib的实现规约和流程衔接。 diff --git a/design/CodeMap.dgml b/design/CodeMap.dgml new file mode 100644 index 0000000..39224de --- /dev/null +++ b/design/CodeMap.dgml @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..4f27eb2 --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/packageIcon.png b/packageIcon.png new file mode 100644 index 0000000..2128805 Binary files /dev/null and b/packageIcon.png differ diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/AbstractConfigurationRepository.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/AbstractConfigurationRepository.cs new file mode 100644 index 0000000..dea1259 --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/AbstractConfigurationRepository.cs @@ -0,0 +1,39 @@ +namespace Masa.BuildingBlocks.Configuration; + +public abstract class AbstractConfigurationRepository : IConfigurationRepository +{ + private readonly ILogger? _logger; + + private readonly List _listeners = new(); + + public abstract SectionTypes SectionType { get; init; } + + public AbstractConfigurationRepository(ILoggerFactory? loggerFactory = null) + => _logger = loggerFactory?.CreateLogger(); + + public abstract Properties Load(); + + public void AddChangeListener(IRepositoryChangeListener listener) + { + if (!_listeners.Contains(listener)) + _listeners.Add(listener); + } + + public void RemoveChangeListener(IRepositoryChangeListener listener) + => _listeners.Remove(listener); + + public void FireRepositoryChange(SectionTypes sectionType, Properties newProperties) + { + foreach (var listener in _listeners) + { + try + { + listener.OnRepositoryChange(sectionType, newProperties); + } + catch (Exception ex) + { + _logger?.LogError($"Failed to invoke repository change listener {listener.GetType()}", ex); + } + } + } +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/ConfigurationTypes.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/ConfigurationTypes.cs new file mode 100644 index 0000000..09f64b1 --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/ConfigurationTypes.cs @@ -0,0 +1,9 @@ +namespace Masa.BuildingBlocks.Configuration; +public enum ConfigurationTypes +{ + Properties = 1, + Text, + Json, + Yaml, + Xml +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs new file mode 100644 index 0000000..3a7343e --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiClient.cs @@ -0,0 +1,12 @@ +namespace Masa.BuildingBlocks.Configuration; +public interface IConfigurationApiClient +{ + Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string environment, string cluster, string appId, string configObject, Action valueChanged); + + Task GetAsync(string environment, string cluster, string appId, string configObject, Action valueChanged); + + Task GetDynamicAsync(string environment, string cluster, string appId, string configObject, Action valueChanged); + + Task GetDynamicAsync(string key); +} + diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiManage.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiManage.cs new file mode 100644 index 0000000..f95c529 --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationApiManage.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Configuration; +public interface IConfigurationApiManage +{ + Task UpdateAsync(string environment, string cluster, string appId, string configObject, object value); +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationRepository.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationRepository.cs new file mode 100644 index 0000000..d363fd2 --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/IConfigurationRepository.cs @@ -0,0 +1,11 @@ +namespace Masa.BuildingBlocks.Configuration; +public interface IConfigurationRepository +{ + SectionTypes SectionType { get; init; } + + Properties Load(); + + void AddChangeListener(IRepositoryChangeListener listener); + + void RemoveChangeListener(IRepositoryChangeListener listener); +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/IMasaConfigurationBuilder.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/IMasaConfigurationBuilder.cs new file mode 100644 index 0000000..1cf192b --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/IMasaConfigurationBuilder.cs @@ -0,0 +1,16 @@ +namespace Masa.BuildingBlocks.Configuration; +public interface IMasaConfigurationBuilder : IConfigurationBuilder +{ + Dictionary GetSectionRelations(); + + /// + /// Mount node information + /// + /// + /// The default is the root node + void AddSection(IConfigurationBuilder configurationBuilder, string? sectionName = null); + + void AddRepository(IConfigurationRepository configurationRepository); + + void AddRelations(params ConfigurationRelationOptions[] relationOptions); +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/IRepositoryChangeListener.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/IRepositoryChangeListener.cs new file mode 100644 index 0000000..ed7a70f --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/IRepositoryChangeListener.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Configuration; +public interface IRepositoryChangeListener +{ + void OnRepositoryChange(SectionTypes sectionType, Properties newProperties); +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/Masa.BuildingBlocks.Configuration.csproj b/src/Configuration/Masa.BuildingBlocks.Configuration/Masa.BuildingBlocks.Configuration.csproj new file mode 100644 index 0000000..406a26a --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/Masa.BuildingBlocks.Configuration.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/Options/ConfigurationRelationOptions.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/Options/ConfigurationRelationOptions.cs new file mode 100644 index 0000000..f0cfb9f --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/Options/ConfigurationRelationOptions.cs @@ -0,0 +1,14 @@ +namespace Masa.BuildingBlocks.Configuration.Options; +public class ConfigurationRelationOptions +{ + public SectionTypes SectionType { get; set; } + + public string? ParentSection { get; set; } + + public string Section { get; set; } = default!; + + /// + /// Object type of mapping node relationship + /// + public Type ObjectType { get; set; } = default!; +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/Options/IMasaConfigurationOptions.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/Options/IMasaConfigurationOptions.cs new file mode 100644 index 0000000..1988c87 --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/Options/IMasaConfigurationOptions.cs @@ -0,0 +1,21 @@ +namespace Masa.BuildingBlocks.Configuration.Options; +/// +/// Automatic mapping relationship specification. +/// When RootSection is Null or an empty string, the configuration will be mounted to the root node. +/// When Section is Null, the configuration will be mounted under the RootSection node, and its node name is class name. +/// If Section is an empty string, it will be directly mounted under the RootSection node +/// +public interface IMasaConfigurationOptions +{ + [JsonIgnore] + string? ParentSection { get; init; } + + /// + /// Node name, adjust the mapping relationship by modifying the node name + /// + [JsonIgnore] + string? Section { get; init; } + + [JsonIgnore] + SectionTypes SectionType { get; init; } +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/Properties.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/Properties.cs new file mode 100644 index 0000000..ac18f6d --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/Properties.cs @@ -0,0 +1,69 @@ +namespace Masa.BuildingBlocks.Configuration; +public class Properties : IEquatable, IEquatable +{ + private readonly Dictionary _dict; + + public Properties() => _dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public Properties(IDictionary? dictionary) => + _dict = dictionary == null + ? new Dictionary(StringComparer.OrdinalIgnoreCase) + : new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); + + public Properties(Properties source) => _dict = source._dict; + + public bool TryGetProperty(string key, [NotNullWhen(true)] out string? value) => _dict.TryGetValue(key, out value); + + public string? GetProperty(string key) + { + _dict.TryGetValue(key, out var result); + + return result; + } + + public ISet GetPropertyNames() => new HashSet(_dict.Keys); + + public override bool Equals(object? obj) + { + if (this is null ^ obj is null) return false; + + if (obj is Properties other) + { + return Equals(other); + } + else + { + return false; + } + } + + public bool Equals(Properties? newProperties) + { + if (newProperties == null) return false; + + return GetHashCode() == newProperties.GetHashCode(); + } + + public static bool operator ==(Properties? x, Properties? y) + { + if (x is null ^ y is null) return false; + + if (x is null) return true; + + return x.Equals(y); + } + + public static bool operator !=(Properties? x, Properties? y) + { + if (x is null ^ y is null) return false; + + if (x is null) return false; + + return !x.Equals(y); + } + + public override int GetHashCode() + { + return _dict.Select(key => key.Key + key.Value).Aggregate(0, (hashCode, next) => HashCode.Combine(hashCode, next)); + } +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/SectionTypes.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/SectionTypes.cs new file mode 100644 index 0000000..14d78ea --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/SectionTypes.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Configuration; +public enum SectionTypes +{ + Local = 1, + ConfigurationAPI +} diff --git a/src/Configuration/Masa.BuildingBlocks.Configuration/_Imports.cs b/src/Configuration/Masa.BuildingBlocks.Configuration/_Imports.cs new file mode 100644 index 0000000..4bcb2ec --- /dev/null +++ b/src/Configuration/Masa.BuildingBlocks.Configuration/_Imports.cs @@ -0,0 +1,6 @@ +global using Masa.BuildingBlocks.Configuration.Options; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.Logging; +global using System.Collections.Concurrent; +global using System.Diagnostics.CodeAnalysis; +global using System.Text.Json.Serialization; diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/AggregateRoot.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/AggregateRoot.cs new file mode 100644 index 0000000..418e50b --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/AggregateRoot.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities; +public abstract class AggregateRoot : Entity, IAggregateRoot +{ + +} + +public class AggregateRoot : Entity, IAggregateRoot +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditAggregateRoot.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditAggregateRoot.cs new file mode 100644 index 0000000..9810345 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditAggregateRoot.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities.Auditing; +public abstract class AuditAggregateRoot : AuditEntity, IAggregateRoot +{ + +} + +public class AuditAggregateRoot : AuditEntity, IAggregateRoot +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditEntity.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditEntity.cs new file mode 100644 index 0000000..889452b --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/AuditEntity.cs @@ -0,0 +1,47 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities.Auditing; + +public abstract class AuditEntity : Entity, IAuditEntity +{ + public bool IsDeleted { get; set; } + + public TUserId Creator { get; set; } = default!; + + public DateTime CreationTime { get; set; } + + public TUserId Modifier { get; set; } = default!; + + public DateTime ModificationTime { get; set; } + + public AuditEntity() => Initialize(); + + public void Initialize() + { + this.CreationTime = this.GetCurrentTime(); + this.ModificationTime = this.GetCurrentTime(); + } + + public virtual DateTime GetCurrentTime() => DateTime.UtcNow; +} + +public class AuditEntity : Entity, IAuditEntity +{ + public bool IsDeleted { get; set; } + + public TUserId Creator { get; set; } = default!; + + public DateTime CreationTime { get; set; } + + public TUserId Modifier { get; set; } = default!; + + public DateTime ModificationTime { get; set; } + + public AuditEntity() => Initialize(); + + public void Initialize() + { + this.CreationTime = this.GetCurrentTime(); + this.ModificationTime = this.GetCurrentTime(); + } + + public virtual DateTime GetCurrentTime() => DateTime.UtcNow; +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditAggregateRoot.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditAggregateRoot.cs new file mode 100644 index 0000000..6756e65 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditAggregateRoot.cs @@ -0,0 +1,4 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities.Auditing; +public interface IAuditAggregateRoot : IAuditEntity +{ +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditEntity.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditEntity.cs new file mode 100644 index 0000000..665de42 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Auditing/IAuditEntity.cs @@ -0,0 +1,11 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities.Auditing; +public interface IAuditEntity : ISoftDelete +{ + TUserId Creator { get; set; } + + DateTime CreationTime { get; set; } + + TUserId Modifier { get; set; } + + DateTime ModificationTime { get; set; } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Entity.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Entity.cs new file mode 100644 index 0000000..78db8eb --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/Entity.cs @@ -0,0 +1,69 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities; + +public abstract class Entity : IEntity, IEquatable, IEquatable +{ + public abstract IEnumerable<(string Name, object Value)> GetKeys(); + + /// + public override string ToString() + { + var keys = GetKeys().ToArray(); + string connector = keys.Length > 1 ? Environment.NewLine : string.Empty; + + return $"{GetType().Name}:{connector}{string.Join(Environment.NewLine, keys.Select(key => $"{key.Name}={key.Value}"))}"; + } + + public override bool Equals(object? obj) + { + if (this is null ^ obj is null) return false; + + if (obj is Entity other) + { + return other.GetKeys().Select(key => key.Value).SequenceEqual(GetKeys().Select(key => key.Value)); + } + else + { + return false; + } + } + + public bool Equals(Entity? other) + { + if (this is null ^ other is null) return false; + + return other!.GetKeys().Select(key => key.Value).SequenceEqual(GetKeys().Select(key => key.Value)); + } + + public override int GetHashCode() + { + return GetKeys().Select(key => key.Value).Aggregate(0, (hashCode, next) => HashCode.Combine(hashCode, next)); + } + + public static bool operator ==(Entity? x, Entity? y) + { + if (x is null ^ y is null) return false; + + if (x is null) return true; + + return x.Equals(y); + } + + public static bool operator !=(Entity? x, Entity? y) + { + if (x is null ^ y is null) return true; + + if (x is null) return false; + + return !x.Equals(y); + } +} + +public class Entity : Entity, IEntity +{ + public TKey Id { get; set; } = default!; + + public override IEnumerable<(string Name, object Value)> GetKeys() + { + yield return ("Id", Id!); + } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IAggregateRoot.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IAggregateRoot.cs new file mode 100644 index 0000000..4ea9d89 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IAggregateRoot.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities; +public interface IAggregateRoot : IEntity +{ + +} + +public interface IAggregateRoot : IEntity, IAggregateRoot +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IEntity.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IEntity.cs new file mode 100644 index 0000000..7570e97 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Entities/IEntity.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Entities; +public interface IEntity +{ + IEnumerable<(string Name, object Value)> GetKeys(); +} + +public interface IEntity : IEntity +{ + TKey Id { get; set; } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainCommand.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainCommand.cs new file mode 100644 index 0000000..8bd55e2 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainCommand.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Events; +public interface IDomainCommand : ICommand, IDomainEvent +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEvent.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEvent.cs new file mode 100644 index 0000000..85a5762 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEvent.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Events; +public interface IDomainEvent : IEvent, ITransaction +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEventBus.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEventBus.cs new file mode 100644 index 0000000..c33ded8 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainEventBus.cs @@ -0,0 +1,8 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Events; +public interface IDomainEventBus : IEventBus +{ + Task Enqueue(TDomentEvent @event) + where TDomentEvent : IDomainEvent; + + Task PublishQueueAsync(); +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainQuery.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainQuery.cs new file mode 100644 index 0000000..4d0bd87 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IDomainQuery.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Events; +public interface IDomainQuery : IDomainEvent, IQuery + where TResult : notnull +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IIntegrationDomainEvent.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IIntegrationDomainEvent.cs new file mode 100644 index 0000000..1629bbd --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Events/IIntegrationDomainEvent.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Events; +public interface IIntegrationDomainEvent : IDomainEvent, IIntegrationEvent +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Masa.BuildingBlocks.DDD.Domain.csproj b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Masa.BuildingBlocks.DDD.Domain.csproj new file mode 100644 index 0000000..851de8e --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Masa.BuildingBlocks.DDD.Domain.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + + + + + + + + + + diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/BaseRepository.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/BaseRepository.cs new file mode 100644 index 0000000..75f46c0 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/BaseRepository.cs @@ -0,0 +1,136 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Repositories; +public abstract class BaseRepository : IRepository, IUnitOfWork + where TEntity : class, IAggregateRoot +{ + #region IRepository + + public abstract ValueTask AddAsync(TEntity entity, CancellationToken cancellationToken = default); + + public virtual async Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await AddAsync(entity, cancellationToken); + } + } + + public virtual async Task FindAsync(params object?[]? keyValues) + { + return await FindAsync(keyValues, default); + } + + public abstract Task FindAsync(object?[]? keyValues, CancellationToken cancellationToken = default); + + public abstract Task FindAsync(Expression> predicate, CancellationToken cancellationToken = default); + + public abstract Task RemoveAsync(TEntity entity, CancellationToken cancellationToken = default); + + public abstract Task RemoveAsync(Expression> predicate, CancellationToken cancellationToken = default); + + public virtual async Task RemoveRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await RemoveAsync(entity, cancellationToken); + } + } + + public abstract Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + + public virtual async Task UpdateRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await UpdateAsync(entity, cancellationToken); + } + } + + public abstract Task> GetListAsync(CancellationToken cancellationToken = default); + + public abstract Task> GetListAsync(Expression> predicate, CancellationToken cancellationToken); + + public abstract Task GetCountAsync(CancellationToken cancellationToken); + + public abstract Task GetCountAsync(Expression> predicate, CancellationToken cancellationToken = default); + + public abstract Task> GetPaginatedListAsync(int skip, int take, Dictionary? sorting, CancellationToken cancellationToken = default); + + public abstract Task> GetPaginatedListAsync(Expression> predicate, int skip, int take, Dictionary? sorting, CancellationToken cancellationToken = default); + + public virtual async Task> GetPaginatedListAsync(PaginatedOptions options, CancellationToken cancellationToken = default) + { + var result = await GetPaginatedListAsync( + (options.Page - 1) * options.PageSize, + options.PageSize <= 0 ? int.MaxValue : options.PageSize, + options.Sorting, + cancellationToken + ); + + var total = await GetCountAsync(cancellationToken); + + return new PaginatedList() + { + Total = total, + Result = result, + TotalPages = (int)Math.Ceiling(total / (decimal)options.PageSize) + }; + } + + public async Task> GetPaginatedListAsync(Expression> predicate, PaginatedOptions options, CancellationToken cancellationToken = default) + { + var result = await GetPaginatedListAsync( + predicate, + (options.Page - 1) * options.PageSize, + options.PageSize <= 0 ? int.MaxValue : options.PageSize, + options.Sorting, + cancellationToken + ); + + var total = await GetCountAsync(predicate, cancellationToken); + + return new PaginatedList() + { + Total = total, + Result = result, + TotalPages = (int)Math.Ceiling(total / (decimal)options.PageSize) + }; + } + + #endregion + + #region IUnitOfWork + + public abstract DbTransaction Transaction { get; } + + public abstract bool TransactionHasBegun { get; } + + public abstract bool UseTransaction { get; set; } + + public bool DisableRollbackOnFailure { get; set; } + + public virtual EntityState EntityState + { + get => UnitOfWork.EntityState; + set => UnitOfWork.EntityState = value; + } + + public virtual CommitState CommitState + { + get => UnitOfWork.CommitState; + set => UnitOfWork.CommitState = value; + } + + public abstract IUnitOfWork UnitOfWork { get; } + + public abstract Task CommitAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask DisposeAsync(); + + public abstract void Dispose(); + + public abstract Task RollbackAsync(CancellationToken cancellationToken = default); + + public abstract Task SaveChangesAsync(CancellationToken cancellationToken = default); + + #endregion +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/IRepository.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/IRepository.cs new file mode 100644 index 0000000..9a03df3 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/IRepository.cs @@ -0,0 +1,68 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Repositories; +public interface IRepository + where TEntity : class, IAggregateRoot +{ + IUnitOfWork UnitOfWork { get; } + + #region Add + + ValueTask AddAsync(TEntity entity, CancellationToken cancellationToken = default); + + Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + #endregion + + #region Update + + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + + Task UpdateRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + #endregion + + #region Remove + + Task RemoveAsync(TEntity entity, CancellationToken cancellationToken = default); + + Task RemoveRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + Task RemoveAsync(Expression> predicate, CancellationToken cancellationToken = default); + + #endregion + + #region Find + + Task FindAsync(params object?[]? keyValues); + + Task FindAsync(object?[]? keyValues, CancellationToken cancellationToken = default); + + Task FindAsync(Expression> predicate, CancellationToken cancellationToken = default); + + #endregion + + #region Get + + Task> GetListAsync(CancellationToken cancellationToken = default); + + Task> GetListAsync(Expression> predicate, CancellationToken cancellationToken = default); + + Task GetCountAsync(CancellationToken cancellationToken = default); + + Task GetCountAsync(Expression> predicate, CancellationToken cancellationToken = default); + + Task> GetPaginatedListAsync(int skip, int take, Dictionary? sorting, CancellationToken cancellationToken = default); + + Task> GetPaginatedListAsync(Expression> predicate, int skip, int take, Dictionary? sorting, CancellationToken cancellationToken = default); + + Task> GetPaginatedListAsync(PaginatedOptions options, CancellationToken cancellationToken = default); + + Task> GetPaginatedListAsync(Expression> predicate, PaginatedOptions options, CancellationToken cancellationToken = default); + + #endregion +} + +public interface IRepository : IRepository + where TAggregateRoot : class, IAggregateRoot +{ + +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedList.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedList.cs new file mode 100644 index 0000000..b9bc73a --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedList.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Repositories; +public class PaginatedList + where TEntity : class, IEntity +{ + public long Total { get; set; } + + public int TotalPages { get; set; } + + public List Result { get; set; } = default!; +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedOptions.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedOptions.cs new file mode 100644 index 0000000..51e1ae8 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Repositories/PaginatedOptions.cs @@ -0,0 +1,9 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Repositories; +public class PaginatedOptions +{ + public int Page { get; set; } + + public int PageSize { get; set; } + + public Dictionary? Sorting { get; set; } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/SeedWork/Enumeration.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/SeedWork/Enumeration.cs new file mode 100644 index 0000000..fce3749 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/SeedWork/Enumeration.cs @@ -0,0 +1,68 @@ +namespace Masa.BuildingBlocks.DDD.Domain.SeedWork; +/// +/// Reference from https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types +/// +public abstract class Enumeration : IComparable +{ + public string Name { get; private set; } + + public int Id { get; private set; } + + protected Enumeration(int id, string name) => (Id, Name) = (id, name); + + public override string ToString() => Name; + + public static IEnumerable GetAll() + where T : Enumeration + { + return typeof(T).GetFields(BindingFlags.Public | + BindingFlags.Static | + BindingFlags.DeclaredOnly) + .Select(f => f.GetValue(null)) + .Cast(); + } + + public override bool Equals(object? obj) + { + if (obj is not Enumeration otherValue) + { + return false; + } + + if (this is null ^ obj is null) return false; + + var typeMatches = GetType().Equals(obj!.GetType()); + var valueMatches = Id.Equals(otherValue.Id); + + return typeMatches && valueMatches; + } + + public override int GetHashCode() => Id.GetHashCode(); + + public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) + { + return Math.Abs(firstValue.Id - secondValue.Id); + } + + public static T FromValue(int value) where T : Enumeration + { + return Parse(value, "value", item => item.Id == value); + } + + public static T FromDisplayName(string displayName) where T : Enumeration + { + return Parse(displayName, "display name", item => item.Name == displayName); + } + + private static T Parse(K value, string description, Func predicate) where T : Enumeration + { + var matchingItem = GetAll().FirstOrDefault(predicate); + + if (matchingItem == null) + throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}"); + + return matchingItem; + } + + public int CompareTo(object? other) => Id.CompareTo(((Enumeration?)other)?.Id); +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Services/IDomainService.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Services/IDomainService.cs new file mode 100644 index 0000000..fef32b8 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Services/IDomainService.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Services; +public interface IDomainService +{ + IDomainEventBus EventBus { get; } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/BaseSpecification.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/BaseSpecification.cs new file mode 100644 index 0000000..79fe991 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/BaseSpecification.cs @@ -0,0 +1,26 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Specifications; +public abstract class BaseSpecification : ISpecification +{ + public BaseSpecification(Expression> whereExpression) + { + WhereExpression = whereExpression; + } + public Expression> WhereExpression { get; } + + public List>> Includes { get; } = + new List>>(); + + public List IncludeStrings { get; } = new List(); + + public abstract bool IsSatisfiedBy(T obj); + + protected virtual void AddInclude(Expression> includeExpression) + { + Includes.Add(includeExpression); + } + + protected virtual void AddInclude(string includeString) + { + IncludeStrings.Add(includeString); + } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/ISpecification.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/ISpecification.cs new file mode 100644 index 0000000..0e703ec --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Specifications/ISpecification.cs @@ -0,0 +1,16 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Specifications; +/// +/// Reference from +/// https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implementation-entity-framework-core +/// https://martinfowler.com/apsupp/spec.pdf +/// +public interface ISpecification +{ + bool IsSatisfiedBy(T obj); + + Expression> WhereExpression { get; } + + List>> Includes { get; } + + List IncludeStrings { get; } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/Values/ValueObject.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Values/ValueObject.cs new file mode 100644 index 0000000..3531890 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/Values/ValueObject.cs @@ -0,0 +1,34 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Values; +public abstract class ValueObject +{ + protected abstract IEnumerable GetEqualityValues(); + + public override bool Equals(object? obj) + { + if (this is null ^ obj is null) return false; + + if (obj is ValueObject entity) + { + return entity.GetEqualityValues().SequenceEqual(GetEqualityValues()); + } + else + { + return false; + } + } + + public override int GetHashCode() + { + return GetEqualityValues().Aggregate(0, (hashCode, next) => HashCode.Combine(hashCode, next)); + } + + public static bool operator ==(ValueObject x, ValueObject y) + { + return x.Equals(y); + } + + public static bool operator !=(ValueObject x, ValueObject y) + { + return !x.Equals(y); + } +} diff --git a/src/DDD/Masa.BuildingBlocks.DDD.Domain/_Imports.cs b/src/DDD/Masa.BuildingBlocks.DDD.Domain/_Imports.cs new file mode 100644 index 0000000..ddc3355 --- /dev/null +++ b/src/DDD/Masa.BuildingBlocks.DDD.Domain/_Imports.cs @@ -0,0 +1,11 @@ +global using Masa.BuildingBlocks.Data.Contracts; +global using Masa.BuildingBlocks.Data.UoW; +global using Masa.BuildingBlocks.DDD.Domain.Entities; +global using Masa.BuildingBlocks.DDD.Domain.Events; +global using Masa.BuildingBlocks.Dispatcher.Events; +global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +global using Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Commands; +global using Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Queries; +global using System.Data.Common; +global using System.Linq.Expressions; +global using System.Reflection; diff --git a/src/Data/Masa.BuildingBlocks.Data.Contracts/ISoftDelete.cs b/src/Data/Masa.BuildingBlocks.Data.Contracts/ISoftDelete.cs new file mode 100644 index 0000000..2514566 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.Contracts/ISoftDelete.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Data.Contracts; +public interface ISoftDelete +{ + bool IsDeleted { get; set; } +} diff --git a/src/Data/Masa.BuildingBlocks.Data.Contracts/Masa.BuildingBlocks.Data.Contracts.csproj b/src/Data/Masa.BuildingBlocks.Data.Contracts/Masa.BuildingBlocks.Data.Contracts.csproj new file mode 100644 index 0000000..0da2a6f --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.Contracts/Masa.BuildingBlocks.Data.Contracts.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/CommitState.cs b/src/Data/Masa.BuildingBlocks.Data.UoW/CommitState.cs new file mode 100644 index 0000000..e0185a8 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/CommitState.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Data.UoW; +public enum CommitState +{ + UnCommited, + Commited +} diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/EntityState.cs b/src/Data/Masa.BuildingBlocks.Data.UoW/EntityState.cs new file mode 100644 index 0000000..97b7377 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/EntityState.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Data.UoW; +public enum EntityState +{ + Unchanged, + Changed +} diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/ITransaction.cs b/src/Data/Masa.BuildingBlocks.Data.UoW/ITransaction.cs new file mode 100644 index 0000000..df058d1 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/ITransaction.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Data.UoW; +public interface ITransaction +{ + [JsonIgnore] + IUnitOfWork? UnitOfWork { get; set; } +} diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/IUnitOfWork.cs b/src/Data/Masa.BuildingBlocks.Data.UoW/IUnitOfWork.cs new file mode 100644 index 0000000..555850d --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/IUnitOfWork.cs @@ -0,0 +1,30 @@ +namespace Masa.BuildingBlocks.Data.UoW; +public interface IUnitOfWork : IDisposable, IAsyncDisposable +{ + DbTransaction Transaction { get; } + + /// + /// Whether the transaction has been opened + /// + bool TransactionHasBegun { get; } + + /// + /// Whether to use transaction + /// + bool UseTransaction { get; set; } + + /// + /// Disable transaction rollback after failure + /// + bool DisableRollbackOnFailure { get; set; } + + EntityState EntityState { get; set; } + + CommitState CommitState { get; set; } + + Task SaveChangesAsync(CancellationToken cancellationToken = default); + + Task CommitAsync(CancellationToken cancellationToken = default); + + Task RollbackAsync(CancellationToken cancellationToken = default); +} diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/Masa.BuildingBlocks.Data.UoW.csproj b/src/Data/Masa.BuildingBlocks.Data.UoW/Masa.BuildingBlocks.Data.UoW.csproj new file mode 100644 index 0000000..463eca5 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/Masa.BuildingBlocks.Data.UoW.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/Data/Masa.BuildingBlocks.Data.UoW/_Imports.cs b/src/Data/Masa.BuildingBlocks.Data.UoW/_Imports.cs new file mode 100644 index 0000000..30f26b4 --- /dev/null +++ b/src/Data/Masa.BuildingBlocks.Data.UoW/_Imports.cs @@ -0,0 +1,2 @@ +global using System.Data.Common; +global using System.Text.Json.Serialization; \ No newline at end of file diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IDispatcherOptions.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IDispatcherOptions.cs new file mode 100644 index 0000000..aa6654d --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IDispatcherOptions.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; +public interface IDispatcherOptions +{ + IServiceCollection Services { get; } +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEvent.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEvent.cs new file mode 100644 index 0000000..a0229e9 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEvent.cs @@ -0,0 +1,16 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; + +public interface IEvent +{ + [JsonIgnore] + Guid Id { get; } + + [JsonIgnore] + DateTime CreationTime { get; } +} + +public interface IEvent : IEvent + where TResult : notnull +{ + TResult Result { get; set; } +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventBus.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventBus.cs new file mode 100644 index 0000000..c543f37 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventBus.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; +public interface IEventBus +{ + Task PublishAsync(TEvent @event) + where TEvent : IEvent; + + IEnumerable GetAllEventTypes(); + + Task CommitAsync(CancellationToken cancellationToken = default); +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventHandler.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventHandler.cs new file mode 100644 index 0000000..e72e941 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IEventHandler.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; +public interface IEventHandler + where TEvent : IEvent +{ + Task HandleAsync(TEvent @event); +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IMiddleware.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IMiddleware.cs new file mode 100644 index 0000000..63e3bd8 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/IMiddleware.cs @@ -0,0 +1,12 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; + +public delegate Task EventHandlerDelegate(); + +/// +/// Middleware is assembled into an event pipeline to handle invoke event and result +/// +public interface IMiddleware + where TEvent : notnull, IEvent +{ + Task HandleAsync(TEvent @event, EventHandlerDelegate next); +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/ISagaEventHandler.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/ISagaEventHandler.cs new file mode 100644 index 0000000..9982ab7 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/ISagaEventHandler.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.Dispatcher.Events; +public interface ISagaEventHandler : IEventHandler + where TEvent : IEvent +{ + Task CancelAsync(TEvent @event); +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/Masa.BuildingBlocks.Dispatcher.Events.csproj b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/Masa.BuildingBlocks.Dispatcher.Events.csproj new file mode 100644 index 0000000..1e07e9f --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/Masa.BuildingBlocks.Dispatcher.Events.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/_Imports.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/_Imports.cs new file mode 100644 index 0000000..9b98d77 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.Events/_Imports.cs @@ -0,0 +1,2 @@ +global using Microsoft.Extensions.DependencyInjection; +global using System.Text.Json.Serialization; diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEvent.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEvent.cs new file mode 100644 index 0000000..63187e8 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEvent.cs @@ -0,0 +1,4 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +public interface IIntegrationEvent : IEvent, ITopic, ITransaction +{ +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEventBus.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEventBus.cs new file mode 100644 index 0000000..da9010e --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IIntegrationEventBus.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +public interface IIntegrationEventBus : IEventBus +{ + +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/ITopic.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/ITopic.cs new file mode 100644 index 0000000..a054489 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/ITopic.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +public interface ITopic +{ + string Topic { get; set; } +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IntegrationEventStates.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IntegrationEventStates.cs new file mode 100644 index 0000000..bf940c9 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/IntegrationEventStates.cs @@ -0,0 +1,8 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +public enum IntegrationEventStates +{ + NotPublished = 0, + InProgress = 1, + Published = 2, + PublishedFailed = 3 +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs new file mode 100644 index 0000000..fcd186a --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs @@ -0,0 +1,29 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents.Logs; +public interface IIntegrationEventLogService +{ + /// + /// Get messages to retry + /// + /// The size of a single event to be retried + /// + /// default: 60s + /// + Task> RetrieveEventLogsFailedToPublishAsync(int retryBatchSize = 200, int maxRetryTimes = 10, int minimumRetryInterval = 60); + + Task SaveEventAsync(IIntegrationEvent @event, DbTransaction transaction); + + Task MarkEventAsPublishedAsync(Guid eventId); + + Task MarkEventAsInProgressAsync(Guid eventId); + + Task MarkEventAsFailedAsync(Guid eventId); + + /// + /// Delete successfully published and expired data + /// + /// + /// + /// + /// + Task DeleteExpiresAsync(DateTime expiresAt, int batchCount = 1000, CancellationToken token = default); +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IntegrationEventLog.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IntegrationEventLog.cs new file mode 100644 index 0000000..de2dc75 --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IntegrationEventLog.cs @@ -0,0 +1,59 @@ +namespace Masa.BuildingBlocks.Dispatcher.IntegrationEvents.Logs; + +public class IntegrationEventLog +{ + public Guid Id { get; private set; } + + public Guid EventId { get; private set; } + + public string EventTypeName { get; private set; } = null!; + + [NotMapped] + public string EventTypeShortName => EventTypeName.Split('.').Last(); + + [NotMapped] + public IIntegrationEvent Event { get; private set; } = null!; + + public IntegrationEventStates State { get; set; } = IntegrationEventStates.NotPublished; + + public int TimesSent { get; set; } = 0; + + public DateTime CreationTime { get; private set; } + + public DateTime ModificationTime { get; set; } + + public string Content { get; private set; } = null!; + + public Guid TransactionId { get; private set; } = Guid.Empty; + + public byte[] RowVersion { get; set; } + + private IntegrationEventLog() + { + Id = Guid.NewGuid(); + Initialize(); + } + + public IntegrationEventLog(IIntegrationEvent @event, Guid transactionId) : this() + { + EventId = @event.Id; + CreationTime = @event.CreationTime; + ModificationTime = @event.CreationTime; + EventTypeName = @event.GetType().FullName!; + Content = System.Text.Json.JsonSerializer.Serialize((object)@event); + TransactionId = transactionId; + } + + public void Initialize() + { + this.CreationTime = this.GetCurrentTime(); + } + + public virtual DateTime GetCurrentTime() => DateTime.UtcNow; + + public IntegrationEventLog DeserializeJsonContent(Type type) + { + Event = (System.Text.Json.JsonSerializer.Deserialize(Content, type) as IIntegrationEvent)!; + return this; + } +} diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Masa.BuildingBlocks.Dispatcher.IntegrationEvents.csproj b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Masa.BuildingBlocks.Dispatcher.IntegrationEvents.csproj new file mode 100644 index 0000000..c530b0d --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Masa.BuildingBlocks.Dispatcher.IntegrationEvents.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/_Imports.cs b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/_Imports.cs new file mode 100644 index 0000000..08e5d6d --- /dev/null +++ b/src/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/_Imports.cs @@ -0,0 +1,4 @@ +global using Masa.BuildingBlocks.Data.UoW; +global using Masa.BuildingBlocks.Dispatcher.Events; +global using System.ComponentModel.DataAnnotations.Schema; +global using System.Data.Common; diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommand.cs b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommand.cs new file mode 100644 index 0000000..f61bfa9 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommand.cs @@ -0,0 +1,4 @@ +namespace Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Commands; +public interface ICommand : IEvent, ITransaction +{ +} diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommandHandler.cs b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommandHandler.cs new file mode 100644 index 0000000..a226831 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Commands/ICommandHandler.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Commands; +public interface ICommandHandler : IEventHandler + where TCommand : ICommand +{ +} diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Masa.BuildingBlocks.ReadWriteSpliting.CQRS.csproj b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Masa.BuildingBlocks.ReadWriteSpliting.CQRS.csproj new file mode 100644 index 0000000..2438d13 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Masa.BuildingBlocks.ReadWriteSpliting.CQRS.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQuery.cs b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQuery.cs new file mode 100644 index 0000000..194eca6 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQuery.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Queries; +public interface IQuery : IEvent + where TResult : notnull +{ +} diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQueryHandler.cs b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQueryHandler.cs new file mode 100644 index 0000000..f940814 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/Queries/IQueryHandler.cs @@ -0,0 +1,5 @@ +namespace Masa.BuildingBlocks.ReadWriteSpliting.CQRS.Queries; +public interface IQueryHandler : IEventHandler + where TCommand : IQuery where TResult : notnull +{ +} diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/_Imports.cs b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/_Imports.cs new file mode 100644 index 0000000..a39a784 --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.CQRS/_Imports.cs @@ -0,0 +1,2 @@ +global using Masa.BuildingBlocks.Data.UoW; +global using Masa.BuildingBlocks.Dispatcher.Events; diff --git a/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing.csproj b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/src/ReadWriteSpliting/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing/Masa.BuildingBlocks.ReadWriteSpliting.EventSourcing.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/AutoCompleteDocument.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/AutoCompleteDocument.cs new file mode 100644 index 0000000..0d68968 --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/AutoCompleteDocument.cs @@ -0,0 +1,21 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete; +public class AutoCompleteDocument +{ + public string Id => IdGenerator(); + + public string Text { get; set; } + + public TValue Value { get; set; } + + public AutoCompleteDocument() + { + } + + public AutoCompleteDocument(string text, TValue value) : this() + { + Text = text; + Value = value; + } + + protected virtual string IdGenerator() => $"[{Value}]{Text}"; +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteClient.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteClient.cs new file mode 100644 index 0000000..8c5c24b --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteClient.cs @@ -0,0 +1,24 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete; +public interface IAutoCompleteClient +{ + Task, TValue>> GetAsync( + string keyword, + AutoCompleteOptions? options = null, + CancellationToken cancellationToken = default); + + Task> GetAsync( + string keyword, + AutoCompleteOptions? options = null, + CancellationToken cancellationToken = default) + where TAudoCompleteDocument : AutoCompleteDocument; + + Task SetAsync( + AutoCompleteDocument[] results, + SetOptions? options = null, + CancellationToken cancellationToken = default); + + Task SetAsync( + TAudoCompleteDocument[] documents, + SetOptions? options = null, + CancellationToken cancellationToken = default) where TAudoCompleteDocument : AutoCompleteDocument ; +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteFactory.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteFactory.cs new file mode 100644 index 0000000..512d88e --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/IAutoCompleteFactory.cs @@ -0,0 +1,7 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete; +public interface IAutoCompleteFactory +{ + IAutoCompleteClient CreateClient(); + + IAutoCompleteClient CreateClient(string name); +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Masa.BuildingBlocks.SearchEngine.AutoComplete.csproj b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Masa.BuildingBlocks.SearchEngine.AutoComplete.csproj new file mode 100644 index 0000000..eb2460e --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Masa.BuildingBlocks.SearchEngine.AutoComplete.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/AutoCompleteOptions.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/AutoCompleteOptions.cs new file mode 100644 index 0000000..0b16135 --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/AutoCompleteOptions.cs @@ -0,0 +1,43 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Options; +public class AutoCompleteOptions +{ + public string Field { get; set; } + + private int _page; + + public int Page + { + get => _page; + set + { + if (value <= 0) + throw new ArgumentException($"{nameof(Page)} must be greater than 0", nameof(Page)); + + _page = value; + } + } + + private int _pageSize; + + public int PageSize + { + get => _pageSize; + set + { + if (value <= 0) + throw new ArgumentException($"{nameof(PageSize)} must be greater than 0", nameof(PageSize)); + + _pageSize = value; + } + } + + public SearchType SearchType { get; } + + public AutoCompleteOptions(SearchType searchType = SearchType.Fuzzy) + { + this.Field = "id"; + this.Page = 1; + this.PageSize = 10; + this.SearchType = searchType; + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/SetOptions.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/SetOptions.cs new file mode 100644 index 0000000..71e387b --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Options/SetOptions.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Options; +public class SetOptions +{ + public bool IsOverride { get; set; } + + public SetOptions(bool isOverride = true) + { + IsOverride = isOverride; + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/GetResponse.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/GetResponse.cs new file mode 100644 index 0000000..80fa5bc --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/GetResponse.cs @@ -0,0 +1,14 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Response; +public class GetResponse : ResponseBase + where TDropdownBox : AutoCompleteDocument +{ + public long Total { get; set; } + + public long TotalPages { get; set; } + + public List Data { get; set; } + + public GetResponse(bool isValid, string message) : base(isValid, message) + { + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/ResponseBase.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/ResponseBase.cs new file mode 100644 index 0000000..54da11a --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/ResponseBase.cs @@ -0,0 +1,13 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Response; +public class ResponseBase +{ + public bool IsValid { get; } + + public string Message { get; } + + public ResponseBase(bool isValid, string message) + { + IsValid = isValid; + Message = message; + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponse.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponse.cs new file mode 100644 index 0000000..924f495 --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponse.cs @@ -0,0 +1,10 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Response; + +public class SetResponse : ResponseBase +{ + public List Items { get; set; } + + public SetResponse(bool isValid, string message) : base(isValid, message) + { + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponseItems.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponseItems.cs new file mode 100644 index 0000000..5aef8dc --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/Response/SetResponseItems.cs @@ -0,0 +1,16 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete.Response; +public class SetResponseItems +{ + public string Id { get; } + + public bool IsValid { get; } + + public string Message { get; } + + public SetResponseItems(string id, bool isValid, string message) + { + Id = id; + IsValid = isValid; + Message = message; + } +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/SearchType.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/SearchType.cs new file mode 100644 index 0000000..a90c5bb --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/SearchType.cs @@ -0,0 +1,6 @@ +namespace Masa.BuildingBlocks.SearchEngine.AutoComplete; +public enum SearchType +{ + Precise = 1, + Fuzzy = 2 +} diff --git a/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/_Imports.cs b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/_Imports.cs new file mode 100644 index 0000000..08c36e2 --- /dev/null +++ b/src/SearchEngine/Masa.BuildingBlocks.SearchEngine.AutoComplete/_Imports.cs @@ -0,0 +1,3 @@ +global using Masa.BuildingBlocks.SearchEngine.AutoComplete.Options; +global using Masa.BuildingBlocks.SearchEngine.AutoComplete.Response; + diff --git a/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/IService.cs b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/IService.cs new file mode 100644 index 0000000..37a5682 --- /dev/null +++ b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/IService.cs @@ -0,0 +1,11 @@ +namespace Masa.BuildingBlocks.Service.MinimalAPIs; +public interface IService +{ + WebApplication App { get; } + + IServiceCollection Services { get; } + + TService? GetService(); + + TService GetRequiredService() where TService : notnull; +} diff --git a/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/Masa.BuildingBlocks.Service.MinimalAPIs.csproj b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/Masa.BuildingBlocks.Service.MinimalAPIs.csproj new file mode 100644 index 0000000..b2aea6a --- /dev/null +++ b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/Masa.BuildingBlocks.Service.MinimalAPIs.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/_Imports.cs b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/_Imports.cs new file mode 100644 index 0000000..4f026ce --- /dev/null +++ b/src/Service/Masa.BuildingBlocks.Service.MinimalAPIs/_Imports.cs @@ -0,0 +1,2 @@ +global using Microsoft.AspNetCore.Builder; +global using Microsoft.Extensions.DependencyInjection; diff --git a/test/Masa.BuildingBlocks.Configuration.Tests/Masa.BuildingBlocks.Configuration.Tests.csproj b/test/Masa.BuildingBlocks.Configuration.Tests/Masa.BuildingBlocks.Configuration.Tests.csproj new file mode 100644 index 0000000..b6abf69 --- /dev/null +++ b/test/Masa.BuildingBlocks.Configuration.Tests/Masa.BuildingBlocks.Configuration.Tests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + false + + + + + + + + + + + + + + + diff --git a/test/Masa.BuildingBlocks.Configuration.Tests/TestProperties.cs b/test/Masa.BuildingBlocks.Configuration.Tests/TestProperties.cs new file mode 100644 index 0000000..1163f59 --- /dev/null +++ b/test/Masa.BuildingBlocks.Configuration.Tests/TestProperties.cs @@ -0,0 +1,63 @@ +namespace Masa.BuildingBlocks.Configuration.Tests; + +[TestClass] +public class TestProperties +{ + [TestMethod] + public void TestEquals() + { + var id = Guid.NewGuid().ToString(); + var x = new Properties(new Dictionary() + { + {"id", id}, + }); + var y = new Properties(new Dictionary() + { + {"id", id}, + }); + var z = new Properties(new Dictionary() + { + {"id2", Guid.NewGuid().ToString()}, + {"id", id} + }); + Assert.IsTrue(x.Equals(y)); + Assert.IsTrue(!x.Equals(null)); + Assert.IsTrue(!x!.Equals(z)); + Assert.IsTrue(!z.Equals(x)); + + var w = new Properties(new Dictionary() + { + {"id", Guid.NewGuid().ToString()}, + }); + + Assert.IsFalse(x!.Equals(w)); + } + + [TestMethod] + public void TestGetHashCode() + { + var id = Guid.NewGuid().ToString(); + Properties x = new(new Dictionary() { { "Id", id } }); + Properties y = new(new Dictionary() { { "Id", id } }); + + Assert.AreEqual(x.GetHashCode(), y.GetHashCode()); + } + + [TestMethod] + public void TestOperator() + { + var id = Guid.NewGuid().ToString(); + Properties x = new(new Dictionary() { { "Id", id } }); + Properties y = new(new Dictionary() { { "Id", id } }); + Properties z = new(new Dictionary() { { "Id", Guid.NewGuid().ToString() } }); + + Assert.IsTrue(x == y); + Assert.IsTrue(x != z); + + Properties? m = null; + Assert.IsTrue(m == null); + Assert.IsTrue(null == m); + Assert.IsFalse(null != m); + Assert.IsFalse(m != null); + } +} diff --git a/test/Masa.BuildingBlocks.Configuration.Tests/_Imports.cs b/test/Masa.BuildingBlocks.Configuration.Tests/_Imports.cs new file mode 100644 index 0000000..974ab12 --- /dev/null +++ b/test/Masa.BuildingBlocks.Configuration.Tests/_Imports.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/test/Masa.BuildingBlocks.DDD.Domain.Tests/Masa.BuildingBlocks.DDD.Domain.Tests.csproj b/test/Masa.BuildingBlocks.DDD.Domain.Tests/Masa.BuildingBlocks.DDD.Domain.Tests.csproj new file mode 100644 index 0000000..92e3319 --- /dev/null +++ b/test/Masa.BuildingBlocks.DDD.Domain.Tests/Masa.BuildingBlocks.DDD.Domain.Tests.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + false + + + + + + + + + + + + + + diff --git a/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestEntity.cs b/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestEntity.cs new file mode 100644 index 0000000..2b6b442 --- /dev/null +++ b/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestEntity.cs @@ -0,0 +1,57 @@ +namespace Masa.BuildingBlocks.DDD.Domain.Tests; + +[TestClass] +public class TestEntity +{ + [TestMethod] + public void TestToString() + { + MASAEntity entity = new() { Id = Guid.Empty }; + Assert.AreEqual("MasaEntity:Id=00000000-0000-0000-0000-000000000000", entity.ToString()); + } + + [TestMethod] + public void TestEquals() + { + var id = Guid.NewGuid(); + MASAEntity x = new() { Id = id }; + MASAEntity y = new() { Id = id }; + + Assert.IsTrue(x.Equals(y)); + Assert.IsTrue(x.Equals((object)y)); + } + + [TestMethod] + public void TestGetHashCode() + { + var id = Guid.NewGuid(); + MASAEntity x = new() { Id = id }; + MASAEntity y = new() { Id = id }; + + Assert.AreEqual(x.GetHashCode(), y.GetHashCode()); + } + + [TestMethod] + public void TestOperator() + { + var id = Guid.NewGuid(); + MASAEntity x = new() { Id = id }; + MASAEntity y = new() { Id = id }; + MASAEntity z = new() { Id = Guid.NewGuid() }; + + Assert.IsTrue(x == y); + Assert.IsTrue(x != z); + + MASAEntity? m = null; + Assert.IsTrue(m == null); + Assert.IsTrue(null == m); + Assert.IsFalse(null != m); + Assert.IsFalse(m != null); + Assert.IsTrue(x != null); + Assert.IsTrue(null != x); + } + + public class MASAEntity : Entity + { + } +} diff --git a/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestValueObject.cs b/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestValueObject.cs new file mode 100644 index 0000000..b1cb737 --- /dev/null +++ b/test/Masa.BuildingBlocks.DDD.Domain.Tests/TestValueObject.cs @@ -0,0 +1,48 @@ +using Masa.BuildingBlocks.DDD.Domain.Values; + +namespace Masa.BuildingBlocks.DDD.Domain.Tests; +[TestClass] +public class TestValueObject +{ + [TestMethod] + public void TestEquals() + { + var id = Guid.NewGuid(); + MasaValueObject x = new() { Id = id }; + MasaValueObject y = new() { Id = id }; + + Assert.IsTrue(x.Equals(y)); + } + + [TestMethod] + public void TestGetHashCode() + { + var id = Guid.NewGuid(); + MasaValueObject x = new() { Id = id }; + MasaValueObject y = new() { Id = id }; + + Assert.AreEqual(x.GetHashCode(), y.GetHashCode()); + } + + [TestMethod] + public void TestOperator() + { + var id = Guid.NewGuid(); + MasaValueObject x = new() { Id = id }; + MasaValueObject y = new() { Id = id }; + MasaValueObject z = new() { Id = Guid.NewGuid() }; + + Assert.IsTrue(x == y); + Assert.IsTrue(x != z); + } + + public class MasaValueObject : ValueObject + { + public Guid Id { get; set; } + + protected override IEnumerable GetEqualityValues() + { + yield return Id; + } + } +} diff --git a/test/Masa.BuildingBlocks.DDD.Domain.Tests/_Imports.cs b/test/Masa.BuildingBlocks.DDD.Domain.Tests/_Imports.cs new file mode 100644 index 0000000..a62310d --- /dev/null +++ b/test/Masa.BuildingBlocks.DDD.Domain.Tests/_Imports.cs @@ -0,0 +1,2 @@ +global using Masa.BuildingBlocks.DDD.Domain.Entities; +global using Microsoft.VisualStudio.TestTools.UnitTesting;