├── .DS_Store ├── .github ├── images │ ├── tyrion.jpg │ └── tyrion.png └── workflows │ ├── build.yml │ └── main.yml ├── .gitignore ├── Directory.Build.props ├── README.md ├── Tyrion.CQRS.sln ├── _config.yml ├── appveyor.yml ├── azure-pipelines.yml └── src ├── .DS_Store ├── LICENSE ├── Tyrion.Tests ├── Category │ ├── Category.cs │ ├── CategoryCommand.cs │ ├── CategoryCommandHandler.cs │ └── CategoryCommandValidator.cs ├── Product │ ├── InativeProductCommand.cs │ ├── LastProductInStockEvent.cs │ ├── Product.cs │ ├── ProductCommandHandler.cs │ ├── ProductDiscountEmailEvent.cs │ ├── ProductGiftEvent.cs │ ├── ProductNotificationHandler.cs │ ├── RemoveProductCommand.cs │ ├── SaveProductCommand.cs │ └── UpdateProductCommand.cs ├── Tyrion.Tests.csproj └── TyrionTests.cs └── Tyrion ├── .DS_Store ├── Extensions ├── ServiceCollectionExtensions.cs └── TypeExtensions.cs ├── Handlers ├── INotification.cs ├── INotificationHandler.cs ├── IRequest.cs └── IRequestHandler.cs ├── ITyrion.cs ├── Results ├── IResult.cs └── Result.cs ├── Tyrion.cs ├── Tyrion.csproj └── Validators └── Validator.cs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaslab-dev/Tyrion/f287193a1eee57bc63912f76877b9281f34bd6c9/.DS_Store -------------------------------------------------------------------------------- /.github/images/tyrion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaslab-dev/Tyrion/f287193a1eee57bc63912f76877b9281f34bd6c9/.github/images/tyrion.jpg -------------------------------------------------------------------------------- /.github/images/tyrion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaslab-dev/Tyrion/f287193a1eee57bc63912f76877b9281f34bd6c9/.github/images/tyrion.png -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: windows-latest 12 | steps: 13 | - name: Set up JDK 11 14 | uses: actions/setup-java@v1 15 | with: 16 | java-version: 1.11 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 20 | - name: Cache SonarCloud packages 21 | uses: actions/cache@v1 22 | with: 23 | path: ~\sonar\cache 24 | key: ${{ runner.os }}-sonar 25 | restore-keys: ${{ runner.os }}-sonar 26 | - name: Cache SonarCloud scanner 27 | id: cache-sonar-scanner 28 | uses: actions/cache@v1 29 | with: 30 | path: .\.sonar\scanner 31 | key: ${{ runner.os }}-sonar-scanner 32 | restore-keys: ${{ runner.os }}-sonar-scanner 33 | - name: Install SonarCloud scanner 34 | if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' 35 | shell: powershell 36 | run: | 37 | New-Item -Path .\.sonar\scanner -ItemType Directory 38 | dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner 39 | - name: Build and analyze 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 42 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 43 | shell: powershell 44 | run: | 45 | .\.sonar\scanner\dotnet-sonarscanner begin /k:"lucasluizss_Tyrion.CQRS" /o:"lucasluizss" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" 46 | dotnet build 47 | dotnet test --collect "Code Coverage" 48 | .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy package 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-and-deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@main 14 | 15 | - name: Set up .NET Core 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: '7.0.x' 19 | 20 | - name: Build with dotnet 21 | run: dotnet build --configuration Release 22 | 23 | - name: Test 24 | run: dotnet test 25 | 26 | - name: Create the Package 27 | run: dotnet pack --configuration Release 28 | 29 | - name: Publish 30 | env: 31 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 32 | run: | 33 | find . -name "*.nupkg" -exec dotnet nuget push {} -k $NUGET_API_KEY -s https://api.nuget.org/v3/index.json \; 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | *.bat 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Mono auto generated files 18 | mono_crash.* 19 | 20 | # Build results 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Rr]elease/ 24 | [Rr]eleases/ 25 | x64/ 26 | x86/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | .vscode/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # StyleCop 67 | StyleCopReport.xml 68 | 69 | # Files built by Visual Studio 70 | *_i.c 71 | *_p.c 72 | *_h.h 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.iobj 77 | *.pch 78 | *.pdb 79 | *.ipdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *_wpftmp.csproj 90 | *.log 91 | *.vspscc 92 | *.vssscc 93 | .builds 94 | *.pidb 95 | *.svclog 96 | *.scc 97 | 98 | # Chutzpah Test files 99 | _Chutzpah* 100 | 101 | # Visual C++ cache files 102 | ipch/ 103 | *.aps 104 | *.ncb 105 | *.opendb 106 | *.opensdf 107 | *.sdf 108 | *.cachefile 109 | *.VC.db 110 | *.VC.VC.opendb 111 | 112 | # Visual Studio profiler 113 | *.psess 114 | *.vsp 115 | *.vspx 116 | *.sap 117 | 118 | # Visual Studio Trace Files 119 | *.e2e 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | tools/** 313 | !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | 354 | # ReportGenerator 355 | coverageOutput/ 356 | result.xml -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | lucasluizss 4 | true 5 | true 6 | true 7 | latest 8 | en 9 | True 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ⚔️ Tyrion 2 | 3 | [![Build Status](https://dev.azure.com/lucasluizss/Tyrion.CQRS/_apis/build/status/lucasluizss.Tyrion.CQRS?branchName=master)](https://dev.azure.com/lucasluizss/Tyrion.CQRS/_build/latest?definitionId=1&branchName=master) 4 | [![GitHub license](https://img.shields.io/github/license/lucasluizss/Tyrion.CQRS)](https://github.com/lucasluizss/Tyrion.CQRS) 5 | [![NuGet](https://img.shields.io/nuget/dt/tyrion.svg)](https://www.nuget.org/packages/tyrion) 6 | [![NuGet](https://img.shields.io/nuget/vpre/tyrion.svg)](https://www.nuget.org/packages/tyrion) 7 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/1e18174734fa415a9e64ef831e87d4b4)](https://www.codacy.com/manual/lucasluizss/Tyrion.CQRS?utm_source=github.com&utm_medium=referral&utm_content=lucasluizss/Tyrion.CQRS&utm_campaign=Badge_Grade) 8 | 9 | Tyrion is an implementation of mediator pattern for dotnet 10 | 11 | ## 👨🏽‍💻 Installing 12 | 13 | - Package Manager 14 | 15 | ```bash 16 | Install-Package Tyrion 17 | ``` 18 | 19 | - .Net CLI 20 | 21 | ```bash 22 | dotnet add package Tyrion 23 | ``` 24 | 25 | - Pakat CLI 26 | 27 | ```bash 28 | paket add Tyrion 29 | ``` 30 | 31 | ## 🧾 Usage 32 | 33 | In your Startup.cs, add Tyrion on your service with some typeof class, for identify the currently assembly project. (Could be any class in your project) like this: 34 | 35 | ```csharp 36 | 37 | services.AddTyrion(typeof(Category)); 38 | 39 | ``` 40 | 41 | When we talk about mediator everything is about requests... Tyrion is abstract for just one interface, where you will handle with multiple concepts types implementing just the IRequest interface for Commands and Queries and INotification interface when you want send multiple notifications events. 42 | 43 | You must keep it simple, so when you are creating a CommandHandle you have to combine your Command request. That way, Tyrion can find quickly the required implementation. 44 | 45 | Samples of requests bellow: 46 | 47 | ```csharp 48 | public class ProductCommand : IRequest { } 49 | 50 | public class CategoryQuery : IRequest { } 51 | 52 | public class CategoryAddedEvent : INotification { } 53 | ``` 54 | 55 | Samples of validation requests bellow (this is an optional implementation): 56 | 57 | ```csharp 58 | public sealed class CategoryCommandValidator : Validator 59 | { 60 | public CategoryCommandValidator() 61 | { 62 | RuleFor(x => x.Name).NotEmpty(); 63 | } 64 | } 65 | ``` 66 | 67 | Samples of Handlers bellow: 68 | 69 | ```csharp 70 | public sealed class CategoryCommandHandler : IRequestHandler 71 | { 72 | public async Task> Execute(CategoryCommand command) 73 | { 74 | return await Result.SuccessedAsync(new Category()); 75 | } 76 | } 77 | 78 | public sealed class CategoryQueryHandler : IRequestHandler 79 | { 80 | public async Task> Execute(CategoryQuery request) 81 | { 82 | return await Result.SuccessedAsync(new Category()); 83 | } 84 | } 85 | 86 | public sealed class CategoryNotificationHandler : INotificationHandler 87 | { 88 | public async Task Publish(CategoryAddedEvent notification) 89 | { 90 | await Task.CompletedTask; 91 | } 92 | } 93 | ``` 94 | 95 | IRequestHandler an receive one or two generics arguments, the first one is your request (Command, Query, etc) all implementing IRequest and the secound one is you return type if you need. 96 | 97 | Case you implement your request validation (using Validator<> class), Tyrion will handle with the validation and return your errors automaticaly. 98 | 99 | That way we can assunrency that we will receive what we are requiring. 100 | 101 | Tyrion allows you to implement multiple handlers in your application service. Just in case you need keep the same context together. 102 | 103 | Something like this: 104 | 105 | ```csharp 106 | public sealed class ProductCommandHandler : IRequestHandler, 107 | IRequestHandler, 108 | IRequestHandler, 109 | IRequestHandler 110 | { 111 | public async Task> Execute(SaveProductCommand request) 112 | { 113 | return await Result.SuccessedAsync(new Product()); 114 | } 115 | 116 | public async Task> Execute(UpdateProductCommand command) 117 | { 118 | return await Result.SuccessedAsync(new Product()); 119 | } 120 | 121 | public async Task Execute(RemoveProductCommand request) 122 | { 123 | return await Result.SuccessedAsync(new Product()); 124 | } 125 | 126 | public async Task Execute(InativeProductCommand request) 127 | { 128 | return await Result.SuccessedAsync(); 129 | } 130 | } 131 | ``` 132 | 133 | I hope you enjoy this. I will work to include little improvements! 134 | 135 | ## 🙋🏽‍♂️ Author 136 | 137 | X: [@lucasluizss](https://x.com/lucasluizss) 138 | Platform: [@lucaslab.dev](https://lucaslab.dev) 139 | 140 | ## 📝 Contributing 141 | 142 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 143 | 144 | Please make sure to update tests as appropriate. 145 | 146 | ## ⚖️ License 147 | 148 | [MIT](https://choosealicense.com/licenses/mit/) 149 | -------------------------------------------------------------------------------- /Tyrion.CQRS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tyrion.Tests", "src\Tyrion.Tests\Tyrion.Tests.csproj", "{6A667415-0257-412B-843E-B5EE4ACFB76A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tyrion", "src\Tyrion\Tyrion.csproj", "{14745A2F-0CF4-4EE6-82BF-354F3A39F67D}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6A667415-0257-412B-843E-B5EE4ACFB76A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {6A667415-0257-412B-843E-B5EE4ACFB76A}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {6A667415-0257-412B-843E-B5EE4ACFB76A}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {6A667415-0257-412B-843E-B5EE4ACFB76A}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {14745A2F-0CF4-4EE6-82BF-354F3A39F67D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {14745A2F-0CF4-4EE6-82BF-354F3A39F67D}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {14745A2F-0CF4-4EE6-82BF-354F3A39F67D}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {14745A2F-0CF4-4EE6-82BF-354F3A39F67D}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {210C3464-543E-4389-A5A1-FBB5546FB249} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | skip_tags: true 5 | build: 6 | verbosity: minimal -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | steps: 13 | - powershell: .\build.ps1 -Target Coverage 14 | displayName: 'Run Tests & Coverage' 15 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaslab-dev/Tyrion/f287193a1eee57bc63912f76877b9281f34bd6c9/src/.DS_Store -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lucas Silva 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Category/Category.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public class Category { } 4 | } -------------------------------------------------------------------------------- /src/Tyrion.Tests/Category/CategoryCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public sealed class CategoryCommand : IRequest 4 | { 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Tyrion.Tests/Category/CategoryCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion.Tests 4 | { 5 | public sealed class CategoryCommandHandler : IRequestHandler 6 | { 7 | public async Task> Execute(CategoryCommand command) 8 | { 9 | return await Result.SuccessAsync(new Category()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Category/CategoryCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Tyrion.Tests 4 | { 5 | public sealed class CategoryCommandValidator : Validator 6 | { 7 | public CategoryCommandValidator() 8 | { 9 | RuleFor(x => x.Name).NotEmpty(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/InativeProductCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public sealed class InativeProductCommand : IRequest { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/LastProductInStockEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public class LastProductInStockEvent : INotification { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public class Product { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/ProductCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion.Tests 4 | { 5 | public sealed class ProductCommandHandler : IRequestHandler, 6 | IRequestHandler, 7 | IRequestHandler, 8 | IRequestHandler 9 | { 10 | public async Task> Execute(SaveProductCommand request) 11 | { 12 | return await Result.SuccessAsync(new Product()); 13 | } 14 | 15 | public async Task> Execute(UpdateProductCommand command) 16 | { 17 | return await Result.SuccessAsync(new Product()); 18 | } 19 | 20 | public async Task Execute(RemoveProductCommand request) 21 | { 22 | return await Result.SuccessAsync(); 23 | } 24 | 25 | public async Task Execute(InativeProductCommand request) 26 | { 27 | return await Result.SuccessAsync(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/ProductDiscountEmailEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public class ProductDiscountEmailEvent : INotification { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/ProductGiftEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public class ProductGiftEvent : INotification { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/ProductNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion.Tests 4 | { 5 | public sealed class ProductNotificationHandler : INotificationHandler, 6 | INotificationHandler, 7 | INotificationHandler 8 | { 9 | public async Task Publish(LastProductInStockEvent request) 10 | { 11 | await Task.CompletedTask.ConfigureAwait(false); 12 | } 13 | 14 | public async Task Publish(ProductGiftEvent request) 15 | { 16 | await Task.CompletedTask.ConfigureAwait(false); 17 | } 18 | 19 | public async Task Publish(ProductDiscountEmailEvent request) 20 | { 21 | await Task.CompletedTask.ConfigureAwait(false); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/RemoveProductCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public sealed class RemoveProductCommand : IRequest { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/SaveProductCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public sealed class SaveProductCommand : IRequest { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Product/UpdateProductCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion.Tests 2 | { 3 | public sealed class UpdateProductCommand : IRequest { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/Tyrion.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Tyrion.Tests/TyrionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace Tyrion.Tests 5 | { 6 | [TestClass] 7 | public class Tests 8 | { 9 | private readonly ITyrion _tyrion; 10 | 11 | public Tests() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddTyrion(typeof(Category)); 16 | 17 | _tyrion = services.BuildServiceProvider().GetRequiredService(); 18 | } 19 | 20 | [TestMethod] 21 | public void TestMethodStuff() 22 | { 23 | var command = new CategoryCommand 24 | { 25 | Name = "stuff" 26 | }; 27 | 28 | var result = _tyrion.Execute(command).Result; 29 | 30 | Assert.IsTrue(result.Successed); 31 | } 32 | 33 | [TestMethod] 34 | public void TestMethodSaveProduct() 35 | { 36 | var command = new SaveProductCommand(); 37 | 38 | var result = _tyrion.Execute(command).Result; 39 | 40 | Assert.IsTrue(result.Successed); 41 | } 42 | 43 | [TestMethod] 44 | public void TestMethodUpdateProduct() 45 | { 46 | var command = new UpdateProductCommand(); 47 | 48 | var result = _tyrion.Execute(command).Result; 49 | 50 | Assert.IsTrue(result.Successed); 51 | } 52 | 53 | [TestMethod] 54 | public void TestMethodRemoveProduct() 55 | { 56 | var command = new RemoveProductCommand(); 57 | 58 | var result = _tyrion.Execute(command).Result; 59 | 60 | Assert.IsTrue(result.Successed); 61 | } 62 | 63 | [TestMethod] 64 | public void TestMethodInativeProduct() 65 | { 66 | var command = new InativeProductCommand(); 67 | 68 | var result = _tyrion.Execute(command).Result; 69 | 70 | Assert.IsTrue(result.Successed); 71 | } 72 | 73 | [TestMethod] 74 | public void TestMethodProductNotification() 75 | { 76 | _tyrion.Publish(new LastProductInStockEvent()).GetAwaiter(); 77 | _tyrion.Publish(new ProductGiftEvent()).GetAwaiter(); 78 | _tyrion.Publish(new ProductDiscountEmailEvent()).GetAwaiter(); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/Tyrion/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaslab-dev/Tyrion/f287193a1eee57bc63912f76877b9281f34bd6c9/src/Tyrion/.DS_Store -------------------------------------------------------------------------------- /src/Tyrion/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace Tyrion 7 | { 8 | public static class ServiceCollectionExtensions 9 | { 10 | private static bool IsRequestHandlers(Type type) => type.Is(typeof(IRequestHandler<,>)) || type.Is(typeof(IRequestHandler<>)) || type.Is(typeof(INotificationHandler<>)); 11 | 12 | public static void AddTyrion(this IServiceCollection services, Type type) 13 | { 14 | services.AddScoped(); 15 | services.AddRequestHandlers(type); 16 | services.AddValidators(type); 17 | } 18 | 19 | private static void AddRequestHandlers(this IServiceCollection services, Type type) => type.Assembly 20 | .GetTypes() 21 | .Where(type => type.GetInterfaces().Any(IsRequestHandlers)).ToList() 22 | .ForEach(type => type.GetInterfaces().Where(IsRequestHandlers).ToList().ForEach(@interface => services.TryAddScoped(@interface, type))); 23 | 24 | private static void AddValidators(this IServiceCollection services, Type type) => type.Assembly 25 | .GetTypes() 26 | .Where(type => type.BaseType.Is(typeof(Validator<>))).ToList() 27 | .ForEach(type => services.TryAddScoped(type.BaseType, type)); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Tyrion/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tyrion 4 | { 5 | public static class TypeExtensions 6 | { 7 | public static bool Is(this Type type, Type typeCompare) => type.IsGenericType && (type.Name.Equals(typeCompare.Name) || type.GetGenericTypeDefinition() == typeCompare); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Tyrion/Handlers/INotification.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion 2 | { 3 | public interface INotification { } 4 | } 5 | -------------------------------------------------------------------------------- /src/Tyrion/Handlers/INotificationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion 4 | { 5 | public interface INotificationHandler where TRequest : INotification 6 | { 7 | Task Publish(TRequest request); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Tyrion/Handlers/IRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion 2 | { 3 | public interface IRequest { } 4 | } -------------------------------------------------------------------------------- /src/Tyrion/Handlers/IRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion 4 | { 5 | public interface IRequestHandler where TRequest : IRequest 6 | { 7 | Task> Execute(TRequest request); 8 | } 9 | 10 | public interface IRequestHandler where TRequest : IRequest 11 | { 12 | Task Execute(TRequest request); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Tyrion/ITyrion.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion 4 | { 5 | public interface ITyrion 6 | { 7 | /// 8 | /// Returns an IResult of TResult where request must be an IRequest. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | Task> Execute(TRequest request) where TRequest : IRequest; 15 | 16 | /// 17 | /// Returns a Task. For use when don't require a return. 18 | /// 19 | /// 20 | /// 21 | /// 22 | Task Execute(TRequest request) where TRequest : IRequest; 23 | 24 | /// 25 | /// Publish a notification for all handlers subscribed. 26 | /// 27 | /// 28 | /// 29 | /// 30 | Task Publish(TRequest notification) where TRequest : INotification; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tyrion/Results/IResult.cs: -------------------------------------------------------------------------------- 1 | namespace Tyrion 2 | { 3 | public interface IResult 4 | { 5 | public bool Successed { get; } 6 | public bool Failed { get; } 7 | public string Message { get; } 8 | } 9 | 10 | public interface IResult : IResult 11 | { 12 | public T Data { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Tyrion/Results/Result.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Tyrion 4 | { 5 | public class Result : IResult 6 | { 7 | protected Result() => Successed = true; 8 | 9 | protected Result(string message, bool successed) 10 | { 11 | Successed = successed; 12 | Failed = !successed; 13 | Message = message; 14 | } 15 | 16 | public bool Successed { get; protected set; } 17 | 18 | public bool Failed { get; protected set; } 19 | 20 | public string Message { get; protected set; } 21 | 22 | public static IResult Success() => new Result(); 23 | 24 | public static IResult Success(string message) => new Result(message, true); 25 | 26 | public static Task SuccessAsync() => Task.FromResult(Success()); 27 | 28 | public static Task SuccessAsync(string message) => Task.FromResult(Success(message)); 29 | 30 | public static IResult Fail(string message) => new Result(message, false); 31 | 32 | public static Task FailAsync(string message) => Task.FromResult(Fail(message)); 33 | } 34 | 35 | public sealed class Result : Result, IResult 36 | { 37 | private Result() => Successed = true; 38 | 39 | private Result(T data, bool successed) 40 | { 41 | Successed = successed; 42 | Failed = !successed; 43 | Data = data; 44 | } 45 | 46 | private Result(string message, bool successed) 47 | { 48 | Successed = successed; 49 | Failed = !successed; 50 | Message = message; 51 | } 52 | 53 | public T Data { get; } 54 | 55 | public new static IResult Success() => new Result(); 56 | 57 | public static IResult Success(T data) => new Result(data, true); 58 | 59 | public static Task> SuccessAsync(T data) => Task.FromResult(Success(data)); 60 | 61 | public new static IResult Fail(string message) => new Result(message, false); 62 | 63 | public new static Task> FailAsync(string message) => Task.FromResult(Fail(message)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Tyrion/Tyrion.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Tyrion 7 | { 8 | public sealed class Tyrion : ITyrion 9 | { 10 | private readonly IServiceProvider _serviceProvider; 11 | 12 | public Tyrion(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; 13 | 14 | public async Task> Execute(TRequest request) where TRequest : IRequest 15 | { 16 | var validator = await ValidateRequestAsync(request); 17 | 18 | if (validator.Failed) 19 | { 20 | return Result.Fail(validator.Message); 21 | } 22 | 23 | var service = _serviceProvider.GetService>(); 24 | 25 | if (service == default) 26 | { 27 | throw new ArgumentException($"Class implementing {nameof(IRequestHandler)} not found or not implemented!"); 28 | } 29 | 30 | return await service.Execute(request).ConfigureAwait(false); 31 | } 32 | 33 | public async Task Execute(TRequest request) where TRequest : IRequest 34 | { 35 | var validator = await ValidateRequestAsync(request); 36 | 37 | if (validator.Failed) 38 | { 39 | return Result.Fail(validator.Message); 40 | } 41 | 42 | var service = _serviceProvider.GetService>(); 43 | 44 | if (service == default) 45 | { 46 | throw new ArgumentException($"Class implementing {nameof(IRequestHandler)} not found or not implemented!"); 47 | } 48 | 49 | return await service.Execute(request).ConfigureAwait(false); 50 | } 51 | 52 | public async Task Publish(TRequest notification) where TRequest : INotification 53 | { 54 | var services = _serviceProvider.GetServices>(); 55 | 56 | if (!services.Any()) 57 | { 58 | throw new ArgumentException($"No class implementing {nameof(INotificationHandler)} not found or not implemented!"); 59 | } 60 | 61 | await Task.WhenAll(services.Select(service => service.Publish(notification))).ConfigureAwait(false); 62 | } 63 | 64 | private async Task ValidateRequestAsync(TRequest request) where TRequest : IRequest 65 | { 66 | var validator = _serviceProvider.GetService>(); 67 | 68 | if (validator == default) 69 | { 70 | return await Result.SuccessAsync(); 71 | } 72 | 73 | return await validator.ValidateAsync(request); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Tyrion/Tyrion.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | 5 | true 6 | Lucas Silva 7 | lucaslab.dev 8 | Tyrion is a mediator implementation for dotnet 9 | https://github.com/lucasluizss/Tyrion.CQRS 10 | mediator;cqrs;dotnet;events;notification 11 | Tyrion 12 | Tyrion.CQRS 13 | LICENSE 14 | 1.1.5 15 | 16 | In this new release, we add some resources, including: 17 | - Compatibility with dotnet standard applications 18 | 19 | https://github.com/lucasluizss/Tyrion.CQRS 20 | tyrion.png 21 | 22 | 23 | 24 | 25 | True 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Tyrion/Validators/Validator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System.Threading.Tasks; 3 | 4 | namespace Tyrion 5 | { 6 | public abstract class Validator : AbstractValidator where T : IRequest 7 | { 8 | private string Message { get; set; } 9 | 10 | public new IResult Validate(T instance) 11 | { 12 | if (Equals(instance, default(T))) 13 | { 14 | return Result.Fail(Message ?? string.Empty); 15 | } 16 | 17 | var result = base.Validate(instance); 18 | 19 | return result.IsValid ? Result.Success() : Result.Fail(Message ?? result.ToString()); 20 | } 21 | 22 | public Task ValidateAsync(T instance) => Task.FromResult(Validate(instance)); 23 | 24 | public void CustomMessage(string message) => Message = message; 25 | } 26 | } --------------------------------------------------------------------------------