├── .assets ├── aurora_architecture.png ├── logo.png └── logo_transparent.png ├── .dockerignore ├── .gitattributes ├── .gitignore ├── Aurora.sln ├── Directory.Build.props ├── README.md └── src ├── Adapters ├── Driven │ └── Database.Mongo │ │ ├── Abstractions │ │ └── Repositories │ │ │ ├── IMongoRepository.cs │ │ │ └── MongoRepository.cs │ │ ├── Context │ │ ├── IMongoContext.cs │ │ └── MongoContext.cs │ │ ├── DataBase.Mongo.csproj │ │ ├── DependencyInjection │ │ └── Extensions │ │ │ └── ServiceCollectionExtensions.cs │ │ ├── HealthChecks │ │ └── MongoHealthCheck.cs │ │ ├── Repositories │ │ ├── InventoryRepository │ │ │ ├── IInventoryRepository.cs │ │ │ └── InventoryRepository.cs │ │ └── OperatorRepository │ │ │ ├── IOperatorRepository.cs │ │ │ └── OperatorRepository.cs │ │ └── Services │ │ ├── InventoryService.cs │ │ └── OperatorService.cs └── Driving │ └── WebApi │ ├── Controllers │ └── v1 │ │ └── OperatorsController.cs │ ├── DependencyInjection │ ├── Extensions │ │ ├── ApplicationBuilderExtensions.cs │ │ └── ServiceCollectionExtensions.cs │ └── Transformers │ │ └── SlugyParametersTransformer.cs │ ├── Dockerfile │ ├── Filters │ └── ResponseFilter.cs │ ├── HostedServices │ └── VerifyExperiedPpeService.cs │ ├── Program.cs │ ├── WebApi.csproj │ ├── appsettings.Development.json │ └── appsettings.json └── Core ├── Application ├── Application.csproj ├── DataTransferObject │ ├── ApplicationsDto.cs │ └── InventoriesDto.cs ├── DependencyInjection │ └── Extensions │ │ └── ServiceCollectionExtension.cs ├── DomainServices │ ├── InventoryDomainService.cs │ ├── ManagerDomainService.cs │ └── OperatorDomainService.cs ├── Envelop │ ├── Paging.cs │ ├── Queryable.cs │ └── Response.cs ├── Extensions │ └── QueryableExtensions.cs ├── NotificationPattern │ └── NotificationContext.cs └── Ports │ ├── DomainServices │ ├── IInventoryDomainService.cs │ └── IOperatorDomainService.cs │ ├── MongoServices │ ├── IInventoryService.cs │ └── IOperatorService.cs │ └── NotificationServices │ └── INotificationContext.cs └── Domain ├── Abstractions ├── Aggregates │ ├── AggregateRoot.cs │ └── IAggregateRoot.cs ├── Entitites │ ├── Entity.cs │ └── IEntity.cs ├── Enumerations │ └── Enumeration.cs ├── Validators │ └── AggregateValidator.cs ├── ValueObjects │ └── ValueObject.cs └── ValueTypes │ └── IValueType.cs ├── Aggregates ├── Employee │ ├── Employee.cs │ ├── EmployeeValidator.cs │ ├── IEmployee.cs │ ├── Manager │ │ ├── Manager.cs │ │ └── ManagerValidator.cs │ └── Operator │ │ ├── Operator.cs │ │ └── OperatorValidator.cs └── Inventory │ ├── Inventory.cs │ └── InventoryValidator.cs ├── Domain.csproj ├── Entities └── Ppe │ ├── Ppe.cs │ └── PpeValidator.cs ├── Enumerations └── WorkShift.cs ├── Serializers └── StructBsonSerializer.cs ├── ValueObjects └── Ppes │ ├── Ppe.cs │ └── PpeValidator.cs └── ValueTypes ├── Name.cs ├── Nin.cs └── Password.cs /.assets/aurora_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexalvess/aurora-api-project/02ae5fa9e38706ecb95aeb059fa061277a114af9/.assets/aurora_architecture.png -------------------------------------------------------------------------------- /.assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexalvess/aurora-api-project/02ae5fa9e38706ecb95aeb059fa061277a114af9/.assets/logo.png -------------------------------------------------------------------------------- /.assets/logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexalvess/aurora-api-project/02ae5fa9e38706ecb95aeb059fa061277a114af9/.assets/logo_transparent.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # Benchmark Results 46 | BenchmarkDotNet.Artifacts/ 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # AxoCover is a Code Coverage Tool 120 | .axoCover/* 121 | !.axoCover/settings.json 122 | 123 | # Visual Studio code coverage results 124 | *.coverage 125 | *.coveragexml 126 | 127 | # NCrunch 128 | _NCrunch_* 129 | .*crunch*.local.xml 130 | nCrunchTemp_* 131 | 132 | # MightyMoose 133 | *.mm.* 134 | AutoTest.Net/ 135 | 136 | # Web workbench (sass) 137 | .sass-cache/ 138 | 139 | # Installshield output folder 140 | [Ee]xpress/ 141 | 142 | # DocProject is a documentation generator add-in 143 | DocProject/buildhelp/ 144 | DocProject/Help/*.HxT 145 | DocProject/Help/*.HxC 146 | DocProject/Help/*.hhc 147 | DocProject/Help/*.hhk 148 | DocProject/Help/*.hhp 149 | DocProject/Help/Html2 150 | DocProject/Help/html 151 | 152 | # Click-Once directory 153 | publish/ 154 | 155 | # Publish Web Output 156 | *.[Pp]ublish.xml 157 | *.azurePubxml 158 | # Note: Comment the next line if you want to checkin your web deploy settings, 159 | # but database connection strings (with potential passwords) will be unencrypted 160 | *.pubxml 161 | *.publishproj 162 | 163 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 164 | # checkin your Azure Web App publish settings, but sensitive information contained 165 | # in these scripts will be unencrypted 166 | PublishScripts/ 167 | 168 | # NuGet Packages 169 | *.nupkg 170 | # The packages folder can be ignored because of Package Restore 171 | **/packages/* 172 | # except build/, which is used as an MSBuild target. 173 | !**/packages/build/ 174 | # Uncomment if necessary however generally it will be regenerated when needed 175 | #!**/packages/repositories.config 176 | # NuGet v3's project.json files produces more ignorable files 177 | *.nuget.props 178 | *.nuget.targets 179 | 180 | # Microsoft Azure Build Output 181 | csx/ 182 | *.build.csdef 183 | 184 | # Microsoft Azure Emulator 185 | ecf/ 186 | rcf/ 187 | 188 | # Windows Store app package directories and files 189 | AppPackages/ 190 | BundleArtifacts/ 191 | Package.StoreAssociation.xml 192 | _pkginfo.txt 193 | *.appx 194 | 195 | # Visual Studio cache files 196 | # files ending in .cache can be ignored 197 | *.[Cc]ache 198 | # but keep track of directories ending in .cache 199 | !*.[Cc]ache/ 200 | 201 | # Others 202 | ClientBin/ 203 | ~$* 204 | *~ 205 | *.dbmdl 206 | *.dbproj.schemaview 207 | *.jfm 208 | *.pfx 209 | *.publishsettings 210 | orleans.codegen.cs 211 | 212 | # Since there are multiple workflows, uncomment next line to ignore bower_components 213 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 214 | #bower_components/ 215 | 216 | # RIA/Silverlight projects 217 | Generated_Code/ 218 | 219 | # Backup & report files from converting an old project file 220 | # to a newer Visual Studio version. Backup files are not needed, 221 | # because we have git ;-) 222 | _UpgradeReport_Files/ 223 | Backup*/ 224 | UpgradeLog*.XML 225 | UpgradeLog*.htm 226 | 227 | # SQL Server files 228 | *.mdf 229 | *.ldf 230 | *.ndf 231 | 232 | # Business Intelligence projects 233 | *.rdl.data 234 | *.bim.layout 235 | *.bim_*.settings 236 | 237 | # Microsoft Fakes 238 | FakesAssemblies/ 239 | 240 | # GhostDoc plugin setting file 241 | *.GhostDoc.xml 242 | 243 | # Node.js Tools for Visual Studio 244 | .ntvs_analysis.dat 245 | node_modules/ 246 | 247 | # Typescript v1 declaration files 248 | typings/ 249 | 250 | # Visual Studio 6 build log 251 | *.plg 252 | 253 | # Visual Studio 6 workspace options file 254 | *.opt 255 | 256 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 257 | *.vbw 258 | 259 | # Visual Studio LightSwitch build output 260 | **/*.HTMLClient/GeneratedArtifacts 261 | **/*.DesktopClient/GeneratedArtifacts 262 | **/*.DesktopClient/ModelManifest.xml 263 | **/*.Server/GeneratedArtifacts 264 | **/*.Server/ModelManifest.xml 265 | _Pvt_Extensions 266 | 267 | # Paket dependency manager 268 | .paket/paket.exe 269 | paket-files/ 270 | 271 | # FAKE - F# Make 272 | .fake/ 273 | 274 | # JetBrains Rider 275 | .idea/ 276 | *.sln.iml 277 | 278 | # CodeRush 279 | .cr/ 280 | 281 | # Python Tools for Visual Studio (PTVS) 282 | __pycache__/ 283 | *.pyc 284 | 285 | # Cake - Uncomment if you are using it 286 | # tools/** 287 | # !tools/packages.config 288 | 289 | # Tabs Studio 290 | *.tss 291 | 292 | # Telerik's JustMock configuration file 293 | *.jmconfig 294 | 295 | # BizTalk build output 296 | *.btp.cs 297 | *.btm.cs 298 | *.odx.cs 299 | *.xsd.cs 300 | -------------------------------------------------------------------------------- /Aurora.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.31911.260 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{79D196F6-144B-46FB-B9E2-79A693C6B30C}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{E7FBB2D8-D595-430A-91DE-AA41C2F338FA}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Core\Application\Application.csproj", "{746926F7-0587-48D0-B98B-54BAE03887CB}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Core\Domain\Domain.csproj", "{5F0379BC-523B-4D03-8C9A-0355B97BFC3F}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Adapters", "Adapters", "{CF4BB0D4-5968-48CE-8891-299CB1242233}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driving", "Driving", "{4F46447A-857A-4655-92A0-0BBBBE195B7E}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "src\Adapters\Driving\WebApi\WebApi.csproj", "{2BAA668C-3B0B-46DB-9792-448CD7D79BB7}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driven", "Driven", "{7E45F5FB-9948-4114-A0D3-0B00F0E176C3}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataBase.Mongo", "src\Adapters\Driven\Database.Mongo\DataBase.Mongo.csproj", "{25E1800D-3FF5-4BB8-96A2-F7594E1A1E57}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {746926F7-0587-48D0-B98B-54BAE03887CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {746926F7-0587-48D0-B98B-54BAE03887CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {746926F7-0587-48D0-B98B-54BAE03887CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {746926F7-0587-48D0-B98B-54BAE03887CB}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {5F0379BC-523B-4D03-8C9A-0355B97BFC3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {5F0379BC-523B-4D03-8C9A-0355B97BFC3F}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {5F0379BC-523B-4D03-8C9A-0355B97BFC3F}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {5F0379BC-523B-4D03-8C9A-0355B97BFC3F}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {2BAA668C-3B0B-46DB-9792-448CD7D79BB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {2BAA668C-3B0B-46DB-9792-448CD7D79BB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {2BAA668C-3B0B-46DB-9792-448CD7D79BB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {2BAA668C-3B0B-46DB-9792-448CD7D79BB7}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {25E1800D-3FF5-4BB8-96A2-F7594E1A1E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {25E1800D-3FF5-4BB8-96A2-F7594E1A1E57}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {25E1800D-3FF5-4BB8-96A2-F7594E1A1E57}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {25E1800D-3FF5-4BB8-96A2-F7594E1A1E57}.Release|Any CPU.Build.0 = Release|Any CPU 46 | EndGlobalSection 47 | GlobalSection(SolutionProperties) = preSolution 48 | HideSolutionNode = FALSE 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {E7FBB2D8-D595-430A-91DE-AA41C2F338FA} = {79D196F6-144B-46FB-B9E2-79A693C6B30C} 52 | {746926F7-0587-48D0-B98B-54BAE03887CB} = {E7FBB2D8-D595-430A-91DE-AA41C2F338FA} 53 | {5F0379BC-523B-4D03-8C9A-0355B97BFC3F} = {E7FBB2D8-D595-430A-91DE-AA41C2F338FA} 54 | {CF4BB0D4-5968-48CE-8891-299CB1242233} = {79D196F6-144B-46FB-B9E2-79A693C6B30C} 55 | {4F46447A-857A-4655-92A0-0BBBBE195B7E} = {CF4BB0D4-5968-48CE-8891-299CB1242233} 56 | {2BAA668C-3B0B-46DB-9792-448CD7D79BB7} = {4F46447A-857A-4655-92A0-0BBBBE195B7E} 57 | {7E45F5FB-9948-4114-A0D3-0B00F0E176C3} = {CF4BB0D4-5968-48CE-8891-299CB1242233} 58 | {25E1800D-3FF5-4BB8-96A2-F7594E1A1E57} = {7E45F5FB-9948-4114-A0D3-0B00F0E176C3} 59 | EndGlobalSection 60 | GlobalSection(ExtensibilityGlobals) = postSolution 61 | SolutionGuid = {EC873EAC-8108-43BF-9551-0C14EE99A556} 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | preview 6 | 7 | 8 | 9 | 10 | 6.0.0 11 | 12 | 13 | 2.14.0 14 | 15 | 16 | 10.3.4 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./.assets/logo_transparent.png) 2 | 3 | # What is Aurora project? 4 | It's an open source project, written in .NET, currently in version 6. 5 | 6 | The project's goal is to show how can we use the Hexagonal Architecture concepts and using some concepts like DDD to create an API. 7 | 8 | ## Business proposal: 9 | This project is a simple PPE Management. The principle idea is to register workers and PPE and, with this data, allow to transfer PPE to a worker. 10 | Besides that, this system allows that you see all the PPE and who has a PPE and notify if the PPE is near to expire. 11 | 12 | ### Abbreviations: 13 | * NIN: National Insurance Number (as CPF in Brazil) 14 | * PPE: Personal Protective Equipment 15 | * DDD: Domain Driven Design 16 | 17 | ## How to use: 18 | 1. Clone this project to into your machine 19 | 2. Run MongoDB container (like on Docker) 20 | 2.2. This project using user-secrets, so you need to configure it 21 | 2.1. Inform the right connection string in project 22 | 3. Finally, build and run the application 23 | 24 | For user-secrets: 25 | ``` 26 | dotnet user-secrets set "ConnectionStrings:MongoDb" "[LOCAL_MONGODB_CONNECTIONSTRING]" 27 | ``` 28 | 29 | ## Technologies: 30 | * .NET 7 31 | * C# previewer 32 | * MongoDB 33 | * FluentValidation 34 | * Swagger 35 | * .NET Core Native DI 36 | * Serilog 37 | 38 | ## Architecture: 39 | * Hexagonal Architecture 40 | * Domain Validations 41 | * Domain Notifications 42 | * Repository Pattern 43 | * Notification Pattern 44 | * Value Types 45 | * Monolith 46 | 47 | ## Principles: 48 | * Domain Driven Design 49 | * Clean Code 50 | * S.O.L.I.D. 51 | 52 | --- 53 | 54 | ## Hexagonal Architecture 55 | ![Design Architectural](./.assets/aurora_architecture.png) 56 | 57 | ### Why did I choose this design? 58 | * It's very easy to include, or exclude, a framework or external library in a separate DLL. 59 | * The focus is in Core layer. So the business rules/your domain stay very uncoupled of external things. 60 | * I'm an enthusiast for this Design 😁🤓 61 | 62 | --- 63 | 64 | ## Why Aurora? 65 | The name Aurora came from the natural event called Aurora Borealis. It is a scientific event described by the interaction between the earth's magnetic layer and energized particles from the solar wind. 66 | 67 | A curiosity about such an event is that what we see in photographs is not always the same image that is seen live. 68 | 69 | For more information, look this [link](https://www.hipercultura.com/fenomenos-naturais/). 70 | 71 | ## About: 72 | The Aurora project was developed by [Alex Alves](https://www.linkedin.com/in/alexalvess/). 73 | 74 | --- 75 | 76 | # References: 77 | * [Start in .NET Core with Layer Architecture, Alex Alves](https://medium.com/@alexalves_85598/criando-uma-api-em-net-core-baseado-na-arquitetura-ddd-2c6a409c686) 78 | * [Organize your Project with Hexagon Architecture - Part 01, Alex Alves](https://alexalvess.medium.com/organizando-seu-projeto-net-com-arquitetura-hexagonal-parte-01-a598662a3818) 79 | * [Organize your Project with Hexagon Architecture - Part 02, Alex Alves](https://alexalvess.medium.com/organizando-seu-projeto-net-com-arquitetura-hexagonal-parte-02-fe9a8ed6ab02) 80 | * [Eventual Shop, a State of the art Distributed System, Antônio Falcão](https://github.com/AntonioFalcaoJr/EDA.CleanArch.DDD.CQRS.EventSourcing) -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Abstractions/Repositories/IMongoRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace DataBase.Mongo.Abstractions.Repositories; 10 | 11 | public interface IMongoRepository 12 | { 13 | Task FindAsync(Expression> predicate, CancellationToken cancellationToken) 14 | where TCollection : class; 15 | 16 | Task> GetAllAsync(List fields, CancellationToken cancellationToken) 17 | where TCollection : class; 18 | 19 | Task Upsert(Expression> predicate, TCollection replacementCollection, CancellationToken cancellationToken) 20 | where TCollection : class; 21 | 22 | Task SaveAsync(TCollection collection, CancellationToken cancellationToken) 23 | where TCollection : class; 24 | 25 | Task SaveManyAsync(IEnumerable collections, CancellationToken cancellationToken) 26 | where TCollection : class; 27 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Abstractions/Repositories/MongoRepository.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Context; 2 | using MongoDB.Bson.Serialization; 3 | using MongoDB.Driver; 4 | using MongoDB.Driver.Linq; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Linq.Expressions; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace DataBase.Mongo.Abstractions.Repositories; 14 | 15 | public abstract class MongoRepository : IMongoRepository 16 | { 17 | private readonly IMongoContext _mongoContext; 18 | 19 | public MongoRepository(IMongoContext mongoContext) 20 | => _mongoContext = mongoContext; 21 | 22 | public Task FindAsync(Expression> predicate, CancellationToken cancellationToken) 23 | where TCollection : class 24 | => _mongoContext.GetCollection().AsQueryable().Where(predicate).FirstOrDefaultAsync(cancellationToken); 25 | 26 | public async Task> GetAllAsync(List fields, CancellationToken cancellationToken) 27 | where TCollection : class 28 | { 29 | var projection = Builders.Projection; 30 | ProjectionDefinition projectionDefinition = default; 31 | 32 | if (fields is not null && fields.Count > 0) 33 | { 34 | projectionDefinition = projection.Combine(fields.Select(field => projection.Include(field))); 35 | 36 | var bsons = await _mongoContext.GetCollection() 37 | .Find(Builders.Filter.Empty) 38 | .Project(projectionDefinition) 39 | .ToListAsync(cancellationToken); 40 | 41 | return bsons.Select(bson => BsonSerializer.Deserialize(bson)); 42 | } 43 | 44 | return await _mongoContext.GetCollection().AsQueryable().ToListAsync(cancellationToken); 45 | } 46 | 47 | public Task Upsert(Expression> predicate, TCollection replacementCollection, CancellationToken cancellationToken) 48 | where TCollection : class 49 | => _mongoContext 50 | .GetCollection() 51 | .ReplaceOneAsync( 52 | filter: predicate, 53 | replacement: replacementCollection, 54 | options: new ReplaceOptions { IsUpsert = true }, 55 | cancellationToken: cancellationToken); 56 | 57 | public Task SaveAsync(TCollection collection, CancellationToken cancellationToken) 58 | where TCollection : class 59 | => _mongoContext.GetCollection().InsertOneAsync(collection, cancellationToken: cancellationToken); 60 | 61 | public Task SaveManyAsync(IEnumerable collections, CancellationToken cancellationToken) 62 | where TCollection : class 63 | => _mongoContext.GetCollection().InsertManyAsync(collections, cancellationToken: cancellationToken); 64 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Context/IMongoContext.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Driver; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DataBase.Mongo.Context; 9 | 10 | public interface IMongoContext 11 | { 12 | IMongoDatabase Database { get; } 13 | IMongoCollection GetCollection(); 14 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Context/MongoContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using MongoDB.Driver; 3 | 4 | namespace DataBase.Mongo.Context; 5 | 6 | public class MongoContext : IMongoContext 7 | { 8 | public MongoContext(IConfiguration configuration) 9 | { 10 | var mongoUrl = new MongoUrl(configuration.GetConnectionString("MongoDb")); 11 | Database = new MongoClient(mongoUrl).GetDatabase(mongoUrl.DatabaseName); 12 | } 13 | 14 | public IMongoDatabase Database { get; } 15 | 16 | public IMongoCollection GetCollection() 17 | => Database.GetCollection(typeof(TCollection).Name); 18 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/DataBase.Mongo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/DependencyInjection/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Application.Ports.MongoServices; 2 | using DataBase.Mongo.Context; 3 | using DataBase.Mongo.HealthChecks; 4 | using DataBase.Mongo.Repositories.OperatorRepository; 5 | using DataBase.Mongo.Services; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace DataBase.Mongo.DependencyInjection.Extensions; 9 | 10 | public static class ServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddMongoDbContext(this IServiceCollection services) 13 | => services.AddSingleton(); 14 | 15 | public static IServiceCollection AddMongoDbRepositories(this IServiceCollection services) 16 | => services 17 | .AddTransient(); 18 | 19 | public static IServiceCollection AddMongoDbServices(this IServiceCollection services) 20 | => services 21 | .AddScoped(); 22 | 23 | public static IHealthChecksBuilder AddMongoHealthCheck(this IHealthChecksBuilder builder) 24 | => builder.AddCheck("MongoDb", tags: new[] { "ready" }); 25 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/HealthChecks/MongoHealthCheck.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Context; 2 | using Microsoft.Extensions.Diagnostics.HealthChecks; 3 | using MongoDB.Bson; 4 | using MongoDB.Driver; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace DataBase.Mongo.HealthChecks; 9 | 10 | public class MongoHealthCheck : IHealthCheck 11 | { 12 | private readonly IMongoContext _mongoContext; 13 | 14 | public MongoHealthCheck(IMongoContext mongoContext) 15 | { 16 | _mongoContext = mongoContext; 17 | } 18 | 19 | public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) 20 | { 21 | var isHealthy = await CheckConnectionAsync(); 22 | 23 | if (isHealthy) 24 | return HealthCheckResult.Healthy("MongoDb healthy"); 25 | return HealthCheckResult.Unhealthy("MongoDb Unhealthy"); 26 | } 27 | 28 | private async Task CheckConnectionAsync() 29 | { 30 | try 31 | { 32 | await _mongoContext.Database.RunCommandAsync((Command)"{ping:1}"); 33 | return true; 34 | } 35 | catch 36 | { 37 | return false; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Repositories/InventoryRepository/IInventoryRepository.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Abstractions.Repositories; 2 | 3 | namespace DataBase.Mongo.Repositories.InventoryRepository; 4 | 5 | public interface IInventoryRepository : IMongoRepository { } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Repositories/InventoryRepository/InventoryRepository.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Abstractions.Repositories; 2 | using DataBase.Mongo.Context; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq.Expressions; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace DataBase.Mongo.Repositories.InventoryRepository; 10 | 11 | public class InventoryRepository : MongoRepository, IInventoryRepository 12 | { 13 | public InventoryRepository(IMongoContext mongoContext) 14 | : base(mongoContext) { } 15 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Repositories/OperatorRepository/IOperatorRepository.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Abstractions.Repositories; 2 | 3 | namespace DataBase.Mongo.Repositories.OperatorRepository; 4 | 5 | public interface IOperatorRepository : IMongoRepository { } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Repositories/OperatorRepository/OperatorRepository.cs: -------------------------------------------------------------------------------- 1 | using DataBase.Mongo.Abstractions.Repositories; 2 | using DataBase.Mongo.Context; 3 | 4 | namespace DataBase.Mongo.Repositories.OperatorRepository; 5 | 6 | public class OperatorRepository : MongoRepository, IOperatorRepository 7 | { 8 | public OperatorRepository(IMongoContext mongoContext) 9 | : base(mongoContext) { } 10 | } 11 | -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Services/InventoryService.cs: -------------------------------------------------------------------------------- 1 | using Application.Ports.MongoServices; 2 | using DataBase.Mongo.Repositories.InventoryRepository; 3 | using Domain.Aggregates.Inventory; 4 | using MongoDB.Bson; 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace DataBase.Mongo.Services; 10 | 11 | public class InventoryService : IInventoryService 12 | { 13 | private readonly IInventoryRepository _inventoryRepository; 14 | 15 | public InventoryService(IInventoryRepository inventoryRepository) 16 | => _inventoryRepository = inventoryRepository; 17 | 18 | public Task SaveNewInventoryAsync(Inventory inventory, CancellationToken cancellationToken) 19 | => _inventoryRepository.SaveAsync(inventory, cancellationToken); 20 | 21 | public Task GetInventoryByIdAsync(ObjectId inventoryId, CancellationToken cancellationToken) 22 | => _inventoryRepository.FindAsync(inventory => inventory.Id.Equals(inventoryId), cancellationToken); 23 | 24 | public Task> GetAllInventoryAsync(CancellationToken cancellationToken) 25 | => default;// _inventoryRepository.GetAllAsync(cancellationToken); 26 | 27 | public Task UpdateInventoryAsync(Inventory inventory, CancellationToken cancellationToken) 28 | => _inventoryRepository.Upsert(item => item.Id.Equals(inventory.Id), inventory, cancellationToken); 29 | } -------------------------------------------------------------------------------- /src/Adapters/Driven/Database.Mongo/Services/OperatorService.cs: -------------------------------------------------------------------------------- 1 | using Application.Ports.MongoServices; 2 | using DataBase.Mongo.Repositories.OperatorRepository; 3 | using Domain.Aggregates.Employee.Operator; 4 | using MongoDB.Bson; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace DataBase.Mongo.Services; 11 | 12 | public class OperatorService : IOperatorService 13 | { 14 | private readonly IOperatorRepository _operatorRepository; 15 | 16 | public OperatorService(IOperatorRepository operatorRepository) 17 | => _operatorRepository = operatorRepository; 18 | 19 | public Task SaveNewOperatorAsync(Operator @operator, CancellationToken cancellationToken) 20 | => _operatorRepository.SaveAsync(@operator, cancellationToken); 21 | 22 | public Task UpdateOperatorAsync(Operator @operator, CancellationToken cancellationToken) 23 | => _operatorRepository.Upsert(prop => prop.Id.Equals(@operator.Id), @operator, cancellationToken); 24 | 25 | public Task GetOperatorByIdAsync(ObjectId workerId, CancellationToken cancellationToken) 26 | => _operatorRepository.FindAsync(prop => prop.Id.Equals(workerId), cancellationToken); 27 | 28 | public Task> GetAllOperators(List fields, CancellationToken cancellationToken) 29 | => _operatorRepository.GetAllAsync(fields, cancellationToken); 30 | 31 | 32 | } -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/Controllers/v1/OperatorsController.cs: -------------------------------------------------------------------------------- 1 | using Application.DataTransferObject; 2 | using Application.Envelop; 3 | using Application.Extensions; 4 | using Application.Ports.DomainServices; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.Routing; 8 | using MongoDB.Bson; 9 | using System; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace WebApi.Controllers.v1 14 | { 15 | [ApiVersion("1")] 16 | [ApiController, Route("api/v{version:apiVersion}/[controller]")] 17 | public class OperatorsController : ControllerBase 18 | { 19 | private readonly IOperatorDomainService _operatorDomainService; 20 | private Queryable _queryable; 21 | 22 | public OperatorsController(IOperatorDomainService operatorDomainService, Queryable queryable) 23 | => (_operatorDomainService, _queryable) = (operatorDomainService, queryable); 24 | 25 | [HttpPost] 26 | public async Task RegisterOperatorAsync([FromBody] RegisterOperatorDto registerOperator, CancellationToken cancellationToken) 27 | { 28 | var operatorId = await _operatorDomainService.RegisterOperatorAsync(registerOperator, cancellationToken); 29 | return CreatedAtRoute(nameof(this.RecoverOperatorByIdAsync), routeValues: new { id = operatorId.ToString() }, null); 30 | } 31 | 32 | [HttpGet(Name = nameof(OperatorsController.RecoverOperatorsAsync))] 33 | public async Task RecoverOperatorsAsync([FromQuery] Queryable queryable, CancellationToken cancellationToken) 34 | { 35 | _queryable.Bind(queryable); 36 | 37 | var operators = await _operatorDomainService.RetrieveOperatorsAsync(cancellationToken); 38 | return Ok(operators); 39 | } 40 | 41 | [HttpGet("{id}", Name = nameof(OperatorsController.RecoverOperatorByIdAsync))] 42 | public async Task RecoverOperatorByIdAsync([FromRoute] string id, CancellationToken cancellationToken) 43 | { 44 | var operatorDetails = await _operatorDomainService.RetrieveOperatorDetailsAsync(id, cancellationToken); 45 | return Ok(operatorDetails); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/DependencyInjection/Extensions/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Application.Envelop; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Diagnostics; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using System; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Routing; 9 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 10 | using Microsoft.Extensions.Diagnostics.HealthChecks; 11 | using WebApi.DependencyInjection.Transformers; 12 | using System.Linq; 13 | using System.Text.Json; 14 | 15 | namespace WebApi.DependencyInjection.Extensions; 16 | 17 | public static class ApplicationBuilderExtensions 18 | { 19 | public static void ConfigureExceptionHandler(this IApplicationBuilder applicationBuilder) 20 | => applicationBuilder.UseExceptionHandler(appBuilder => appBuilder.Run(async httpContext => 21 | { 22 | const string ERROR_MESSAGE = "Ocorreu um erro! Estamos trabalhando para solucionar a indisponibilidade. Por favor, tente novamente mais tarde."; 23 | 24 | var exception = httpContext.Features.Get()?.Error; 25 | 26 | httpContext.Response.StatusCode = exception switch 27 | { 28 | OperationCanceledException or TaskCanceledException => 499, 29 | _ => (int)HttpStatusCode.InternalServerError 30 | }; 31 | 32 | await httpContext.Response.WriteAsJsonAsync(new Response(ERROR_MESSAGE)); 33 | })); 34 | 35 | public static void ConfigureHealthChecks(this IEndpointRouteBuilder routeBuilder) 36 | { 37 | routeBuilder.MapHealthChecks("health/live", new HealthCheckOptions 38 | { 39 | Predicate = _ => true 40 | }); 41 | 42 | routeBuilder.MapHealthChecks("health/ready", new HealthCheckOptions 43 | { 44 | Predicate = healthCheck => healthCheck.Tags.Contains("ready"), 45 | ResponseWriter = WriteResponse 46 | }); 47 | } 48 | 49 | private static Task WriteResponse(HttpContext httpContext, HealthReport healthReport) 50 | { 51 | var slugify = new SlugyParametersTransformer(); 52 | 53 | var body = new 54 | { 55 | status = healthReport.Status.ToString(), 56 | results = healthReport.Entries.Select(entry => new 57 | { 58 | status = entry.Value.Status.ToString(), 59 | description = slugify.TransformOutbound(entry.Key.Replace("HealthCheck", string.Empty)) 60 | }) 61 | }; 62 | 63 | httpContext.Response.ContentType = "application/json"; 64 | 65 | return httpContext.Response.WriteAsync(JsonSerializer.Serialize(body, options: new JsonSerializerOptions 66 | { 67 | WriteIndented = true 68 | }).ToString()); 69 | } 70 | } -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/DependencyInjection/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace WebApi.DependencyInjection.Extensions; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static void ConfigureApiVersioning(this IServiceCollection services) 9 | => services 10 | .AddApiVersioning(options => 11 | { 12 | options.DefaultApiVersion = new ApiVersion(1, 0); 13 | options.ReportApiVersions = true; 14 | options.AssumeDefaultVersionWhenUnspecified = true; 15 | }) 16 | .AddVersionedApiExplorer(options => 17 | { 18 | options.GroupNameFormat = "'v'VVV"; 19 | options.SubstituteApiVersionInUrl= true; 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/DependencyInjection/Transformers/SlugyParametersTransformer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Routing; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace WebApi.DependencyInjection.Transformers; 5 | 6 | public class SlugyParametersTransformer : IOutboundParameterTransformer 7 | { 8 | public string TransformOutbound(object value) 9 | => Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 7 | WORKDIR /src 8 | COPY ["src/Adapters/Driving/WebApi/WebApi.csproj", "src/Adapters/Driving/WebApi/"] 9 | RUN dotnet restore "src/Adapters/Driving/WebApi/WebApi.csproj" 10 | COPY . . 11 | WORKDIR "/src/src/Adapters/Driving/WebApi" 12 | RUN dotnet build "WebApi.csproj" -c Release -o /app/build 13 | 14 | FROM build AS publish 15 | RUN dotnet publish "WebApi.csproj" -c Release -o /app/publish 16 | 17 | FROM base AS final 18 | WORKDIR /app 19 | COPY --from=publish /app/publish . 20 | ENTRYPOINT ["dotnet", "WebApi.dll"] -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/Filters/ResponseFilter.cs: -------------------------------------------------------------------------------- 1 | using Application.Envelop; 2 | using Application.Extensions; 3 | using Application.Ports.NotificationServices; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Filters; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using Newtonsoft.Json.Serialization; 9 | using System.Linq; 10 | using System.Net; 11 | using System.Text.Encodings.Web; 12 | using System.Text.Json; 13 | using System.Text.Json.Serialization; 14 | using System.Text.Unicode; 15 | 16 | namespace WebApi.Filters; 17 | 18 | public class ResponseFilter : IResultFilter 19 | { 20 | private readonly INotificationContext _notificationContext; 21 | private readonly Application.Envelop.Queryable _queryable; 22 | 23 | public ResponseFilter(INotificationContext notificationContext, Application.Envelop.Queryable queryable) 24 | => (_notificationContext, _queryable) = (notificationContext, queryable); 25 | 26 | public void OnResultExecuted(ResultExecutedContext context) 27 | { 28 | // Do nothing 29 | } 30 | 31 | public void OnResultExecuting(ResultExecutingContext context) 32 | { 33 | if (_notificationContext.HasNotifications) 34 | { 35 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; 36 | context.HttpContext.Response.ContentType = "application/json; charset=utf-8"; 37 | 38 | var contentResponse = new Response(_notificationContext.Notifications.Select(notification => notification.ErrorMessage).ToList()); 39 | var serializeOptions = new JsonSerializerOptions 40 | { 41 | Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic), 42 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 43 | WriteIndented = true, 44 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull 45 | }; 46 | 47 | context.Result = new JsonResult(contentResponse, serializeOptions); 48 | } 49 | else 50 | { 51 | //var result = _queryable.SelectFields(((ObjectResult)context.Result).Value); 52 | //if (result != default) 53 | // context.Result = new ObjectResult(result); 54 | 55 | if(_queryable.Wrap) 56 | context.Result = new ObjectResult(new Response(((ObjectResult)context.Result).Value)); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/HostedServices/VerifyExperiedPpeService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace WebApi.HostedServices 6 | { 7 | public class VerifyExperiedPpeService : IHostedService 8 | { 9 | public Task StartAsync(CancellationToken cancellationToken) 10 | { 11 | return Task.CompletedTask; 12 | } 13 | 14 | public Task StopAsync(CancellationToken cancellationToken) 15 | { 16 | return Task.CompletedTask; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Application.DependencyInjection.Extensions; 2 | using Application.Envelop; 3 | using DataBase.Mongo.DependencyInjection.Extensions; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Mvc.ApplicationModels; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Serilog; 10 | using System; 11 | using System.Reflection; 12 | using WebApi.DependencyInjection.Extensions; 13 | using WebApi.DependencyInjection.Transformers; 14 | using WebApi.HostedServices; 15 | 16 | var builder = WebApplication.CreateBuilder(args); 17 | 18 | builder.Host 19 | .UseDefaultServiceProvider((context, provider) => 20 | { 21 | provider.ValidateScopes = 22 | provider.ValidateOnBuild = 23 | context.HostingEnvironment.IsDevelopment(); 24 | }) 25 | .ConfigureAppConfiguration((context, configurationBuilder) => 26 | { 27 | configurationBuilder 28 | .AddUserSecrets(Assembly.GetExecutingAssembly()) 29 | .AddEnvironmentVariables(); 30 | }) 31 | .UseSerilog((context, loggerConfiguration) => 32 | { 33 | loggerConfiguration 34 | .ReadFrom 35 | .Configuration(context.Configuration); 36 | }) 37 | .ConfigureServices((context, services) => 38 | { 39 | services.AddRouting(options => options.LowercaseUrls = false); 40 | 41 | services.AddControllers(options => 42 | { 43 | options.Conventions.Add(new RouteTokenTransformerConvention(new SlugyParametersTransformer())); 44 | options.SuppressAsyncSuffixInActionNames = true; 45 | }); 46 | 47 | services.AddHostedService(); 48 | 49 | services.AddEndpointsApiExplorer(); 50 | 51 | services 52 | .ConfigureApiVersioning(); 53 | 54 | services.AddSwaggerGen(); 55 | 56 | services 57 | .AddMongoDbContext() 58 | .AddMongoDbRepositories() 59 | .AddMongoDbServices(); 60 | 61 | services 62 | .AddDomainServices(); 63 | 64 | services 65 | .AddNotificationContext(); 66 | 67 | services 68 | .AddScoped(); 69 | 70 | services 71 | .AddHealthChecks() 72 | .AddMongoHealthCheck(); 73 | }); 74 | 75 | using var app = builder.Build(); 76 | 77 | try 78 | { 79 | app.UseSwagger(); 80 | app.UseSwaggerUI(); 81 | 82 | app.ConfigureExceptionHandler(); 83 | 84 | app.UseHttpsRedirection(); 85 | 86 | app.UseAuthorization(); 87 | 88 | app.UseSerilogRequestLogging(); 89 | 90 | app.ConfigureHealthChecks(); 91 | 92 | app.MapControllers(); 93 | 94 | await app.RunAsync(); 95 | } 96 | catch (Exception ex) 97 | { 98 | await app.StopAsync(); 99 | } 100 | finally 101 | { 102 | await app.DisposeAsync(); 103 | } -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 27e56999-fe25-431c-a726-45f383faacd7 5 | Windows 6 | ..\..\..\.. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Information", 5 | "Override": { 6 | "Default": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "WriteTo": [ 11 | { 12 | "Name": "Console", 13 | "Args": { 14 | "Theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console" 15 | } 16 | } 17 | ], 18 | "Enrich": [ 19 | "FromLogContext", 20 | "WithMachineName", 21 | "WithThreadId" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /src/Adapters/Driving/WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/Core/Application/Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Core/Application/DataTransferObject/ApplicationsDto.cs: -------------------------------------------------------------------------------- 1 | using Domain.Enumerations; 2 | using MongoDB.Bson; 3 | using System; 4 | 5 | namespace Application.DataTransferObject; 6 | 7 | public record RegisterOperatorDto(string Name, string Password, DateTime BirthDate, string Nin); 8 | 9 | public record DistributeEpiDto(ObjectId epiId); 10 | 11 | public record RetrieveOperatorDetailsDto(string Name, DateTime BirthDate, string Nin, WorkShift WorkShift, bool IsActive, DateTime AdmissionDate); 12 | 13 | public record RetrieveOperators(string Name, DateTime? BirthDate, string Nin, WorkShift? WorkShift); 14 | -------------------------------------------------------------------------------- /src/Core/Application/DataTransferObject/InventoriesDto.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using System; 3 | 4 | namespace Application.DataTransferObject; 5 | 6 | public record CreatePpeDto(string Name, string Description, DateOnly ManufacturingDate, long Durability, string ApprovalCertificate); 7 | 8 | public record CreateInventoryDto(CreatePpeDto CreatePpe, long Quantity); 9 | 10 | public record AddPpeDto(ObjectId InventoryId, long Quantity); -------------------------------------------------------------------------------- /src/Core/Application/DependencyInjection/Extensions/ServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using Application.DomainServices; 2 | using Application.NotificationPattern; 3 | using Application.Ports.DomainServices; 4 | using Application.Ports.NotificationServices; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Application.DependencyInjection.Extensions; 8 | 9 | public static class ServiceCollectionExtension 10 | { 11 | public static IServiceCollection AddDomainServices(this IServiceCollection services) 12 | => services 13 | .AddScoped(); 14 | 15 | public static IServiceCollection AddNotificationContext(this IServiceCollection services) 16 | => services 17 | .AddScoped(); 18 | } 19 | -------------------------------------------------------------------------------- /src/Core/Application/DomainServices/InventoryDomainService.cs: -------------------------------------------------------------------------------- 1 | using Application.DataTransferObject; 2 | using Application.Ports.DomainServices; 3 | using Application.Ports.MongoServices; 4 | using Domain.Aggregates.Inventory; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Application.DomainServices; 9 | 10 | public class InventoryDomainService : IInventoryDomainService 11 | { 12 | private readonly IInventoryService _inventoryService; 13 | 14 | public InventoryDomainService(IInventoryService inventoryService) 15 | => _inventoryService = inventoryService; 16 | 17 | public async Task AddMorePpeAsync(AddPpeDto addPpeDto, CancellationToken cancellationToken) 18 | { 19 | var inventory = await _inventoryService.GetInventoryByIdAsync(addPpeDto.InventoryId, cancellationToken); 20 | inventory.AddAvailableQuantity(addPpeDto.Quantity); 21 | 22 | await _inventoryService.UpdateInventoryAsync(inventory, cancellationToken); 23 | } 24 | 25 | public async Task CreateNewInventoryAsync(CreateInventoryDto createInventoryDto, CancellationToken cancellationToken) 26 | { 27 | Inventory inventory = new() 28 | { 29 | Ppe = new() 30 | { 31 | Name = createInventoryDto.CreatePpe.Name, 32 | Description = createInventoryDto.CreatePpe.Description, 33 | Durability = createInventoryDto.CreatePpe.Durability, 34 | ManufacturingDate = createInventoryDto.CreatePpe.ManufacturingDate, 35 | ApprovalCertificate = createInventoryDto.CreatePpe.ApprovalCertificate 36 | } 37 | }; 38 | 39 | inventory.AddAvailableQuantity(createInventoryDto.Quantity); 40 | 41 | await _inventoryService.SaveNewInventoryAsync(inventory, cancellationToken); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Core/Application/DomainServices/ManagerDomainService.cs: -------------------------------------------------------------------------------- 1 | namespace Application.DomainServices; 2 | 3 | public class ManagerDomainService 4 | { 5 | } -------------------------------------------------------------------------------- /src/Core/Application/DomainServices/OperatorDomainService.cs: -------------------------------------------------------------------------------- 1 | using Application.DataTransferObject; 2 | using Application.Ports.DomainServices; 3 | using Application.Ports.MongoServices; 4 | using Application.Ports.NotificationServices; 5 | using Domain.Aggregates.Employee.Operator; 6 | using MongoDB.Bson; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace Application.DomainServices; 14 | 15 | public class OperatorDomainService : IOperatorDomainService 16 | { 17 | private readonly IOperatorService _operatorService; 18 | private readonly INotificationContext _notificationContext; 19 | private readonly Envelop.Queryable _queryable; 20 | 21 | public OperatorDomainService(IOperatorService operatorService, INotificationContext notificationContext, Envelop.Queryable queryable) 22 | => (_operatorService, _notificationContext, _queryable) = (operatorService, notificationContext, queryable); 23 | 24 | public async Task RegisterOperatorAsync(RegisterOperatorDto registerOperatorDto, CancellationToken cancellationToken) 25 | { 26 | Operator @operator = new() 27 | { 28 | BirthDate = registerOperatorDto.BirthDate, 29 | Name = registerOperatorDto.Name, 30 | Nin = registerOperatorDto.Nin, 31 | Password = registerOperatorDto.Password 32 | }; 33 | 34 | if (@operator.IsValid is false) 35 | return default; 36 | 37 | await _operatorService.SaveNewOperatorAsync(@operator, cancellationToken); 38 | 39 | return @operator.Id; 40 | } 41 | 42 | public async Task RetrieveOperatorDetailsAsync(string operatorId, CancellationToken cancellationToken) 43 | { 44 | var @operator = await _operatorService.GetOperatorByIdAsync(new ObjectId(operatorId), cancellationToken); 45 | 46 | if(@operator is null) 47 | { 48 | _notificationContext.AddNotification("This operator was not found."); 49 | return default; 50 | } 51 | 52 | return new( 53 | @operator.Name.ToString(), 54 | @operator.BirthDate, 55 | @operator.Nin.ToString(), 56 | @operator.WorkShift, 57 | @operator.Active, 58 | @operator.AdmissionDate); 59 | } 60 | 61 | public async Task> RetrieveOperatorsAsync(CancellationToken cancellationToken) 62 | { 63 | var operators = await _operatorService.GetAllOperators(_queryable.Fields, cancellationToken); 64 | 65 | if(!operators?.Any() ?? true) 66 | { 67 | _notificationContext.AddNotification("No operators found."); 68 | return default; 69 | } 70 | 71 | return operators.Select(@operator => 72 | new RetrieveOperators( 73 | @operator.Name.ToString(), 74 | @operator.BirthDate == default ? null : @operator.BirthDate, 75 | @operator.Nin.ToString(), 76 | @operator.WorkShift)); 77 | } 78 | 79 | //public async Task DistributePpesAsync(IReadOnlyCollection distributeEpisDto, CancellationToken cancellationToken) 80 | //{ 81 | 82 | //} 83 | } -------------------------------------------------------------------------------- /src/Core/Application/Envelop/Paging.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Application.Envelop 8 | { 9 | public record Paging 10 | { 11 | public string First { get; init; } 12 | 13 | public string Last { get; init; } 14 | 15 | public string Next { get; init; } 16 | 17 | public string Previous { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Core/Application/Envelop/Queryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Application.Envelop 8 | { 9 | public record Queryable 10 | { 11 | public bool Wrap { get; set; } 12 | 13 | public List Fields { get; set; } 14 | 15 | public IDictionary Filter { get; set; } 16 | 17 | public IList Sort { get; set; } 18 | 19 | public IList Search { get; set; } 20 | 21 | public Page Page { get; set; } 22 | 23 | public IDictionary GetSortFields() 24 | { 25 | return Sort.Select(filed => 26 | { 27 | var order = filed.StartsWith("-") ? -1 : 1; 28 | filed = filed[1..]; 29 | 30 | return new KeyValuePair(order, filed); 31 | }).ToDictionary(key => key.Key, value => value.Value); 32 | } 33 | } 34 | 35 | public record Page(long Offset, long Limit); 36 | } 37 | -------------------------------------------------------------------------------- /src/Core/Application/Envelop/Response.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Application.Envelop; 5 | 6 | public record Response 7 | { 8 | private List _errors; 9 | 10 | public Response(object data = default) 11 | => Data = data; 12 | 13 | public Response(string errorMessage) 14 | => AddError(errorMessage); 15 | 16 | public Response(IList errorMessages) 17 | => AddErrors(errorMessages); 18 | 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 20 | public object Data { get; init; } 21 | 22 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 23 | public IReadOnlyCollection Errors 24 | => _errors?.AsReadOnly(); 25 | 26 | public void AddError(string errorMessage) 27 | { 28 | if(_errors is null) 29 | _errors = new List(); 30 | 31 | _errors.Add(errorMessage); 32 | } 33 | 34 | public void AddErrors(IList errorMessages) 35 | { 36 | if (_errors is null) 37 | _errors = new List(); 38 | 39 | _errors.AddRange(errorMessages); 40 | } 41 | } -------------------------------------------------------------------------------- /src/Core/Application/Extensions/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using Application.Envelop; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using Newtonsoft.Json.Serialization; 5 | using System.Linq; 6 | 7 | namespace Application.Extensions; 8 | 9 | public static class QueryableExtensions 10 | { 11 | public static void Bind(this Envelop.Queryable queryableScoped, Envelop.Queryable queryable) 12 | { 13 | queryableScoped.Filter = queryable.Filter; 14 | queryableScoped.Page = queryable.Page; 15 | queryableScoped.Fields = queryable.Fields; 16 | queryableScoped.Sort = queryable.Sort; 17 | queryableScoped.Search = queryable.Search; 18 | queryableScoped.Wrap = queryable.Wrap; 19 | } 20 | 21 | public static object SelectFields(this Envelop.Queryable queryable, object response) 22 | { 23 | if (queryable.Fields?.Any() ?? false) 24 | { 25 | var jsonResult = JsonConvert.SerializeObject(response, new JsonSerializerSettings 26 | { 27 | ContractResolver = new DefaultContractResolver 28 | { 29 | NamingStrategy = new CamelCaseNamingStrategy() 30 | } 31 | }); 32 | 33 | var jToken = JToken.Parse(jsonResult); 34 | 35 | if (jToken is JObject) 36 | { 37 | var jObject = JsonConvert.DeserializeObject(jsonResult); 38 | 39 | foreach (var fieldToRemove in queryable.Fields) 40 | jObject.Property(fieldToRemove).Remove(); 41 | 42 | return jObject; 43 | } 44 | else 45 | { 46 | var jArray = JsonConvert.DeserializeObject(jsonResult); 47 | var newJArray = new JArray(); 48 | 49 | 50 | jArray.Select(item => (JObject)item) 51 | .ToList() 52 | .ForEach(field => 53 | { 54 | var newJObject = new JObject(); 55 | queryable.Fields.ForEach(fieldToAdd => 56 | { 57 | var jToken = field.SelectToken(fieldToAdd); 58 | 59 | if (fieldToAdd.Contains('.')) 60 | BuildJOBject(fieldToAdd.Split("."), jToken, newJObject); 61 | else 62 | newJObject.Add(fieldToAdd, jToken); 63 | 64 | }); 65 | newJArray.Add(newJObject); 66 | }); 67 | 68 | return newJArray; 69 | } 70 | } 71 | 72 | return default; 73 | } 74 | 75 | private static JObject BuildJOBject(string[] fields, JToken jToken, JObject jObject) 76 | { 77 | if (fields.Length == 1) 78 | { 79 | var newJObject = new JObject(); 80 | newJObject.Add(fields.First(), jToken); 81 | return newJObject; 82 | } 83 | 84 | jObject.Add(fields.First(), BuildJOBject(fields.Skip(1).ToArray(), jToken, jObject)); 85 | 86 | return jObject; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Core/Application/NotificationPattern/NotificationContext.cs: -------------------------------------------------------------------------------- 1 | using Application.Ports.NotificationServices; 2 | using FluentValidation.Results; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Application.NotificationPattern; 7 | 8 | public class NotificationContext : INotificationContext 9 | { 10 | private List _notifications; 11 | 12 | public NotificationContext() 13 | => _notifications = new List(); 14 | 15 | public IReadOnlyCollection Notifications 16 | => _notifications?.AsReadOnly(); 17 | 18 | public bool HasNotifications 19 | => _notifications.Any(); 20 | 21 | public void AddNotification(string errorMessage) 22 | => _notifications.Add(new ValidationFailure(default, errorMessage)); 23 | 24 | public void AddNotification(ValidationFailure validationFailure) 25 | => VerifyAndAddNotification(validationFailure); 26 | 27 | public void AddNotification(ValidationResult validationResult) 28 | => validationResult.Errors.ForEach(failure => VerifyAndAddNotification(failure)); 29 | 30 | private void VerifyAndAddNotification(ValidationFailure validationFailure) 31 | { 32 | if (_notifications.Any(notification => notification.PropertyName.Equals(validationFailure.PropertyName))) 33 | return; 34 | 35 | _notifications.Add(validationFailure); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Core/Application/Ports/DomainServices/IInventoryDomainService.cs: -------------------------------------------------------------------------------- 1 | using Application.DataTransferObject; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Application.Ports.DomainServices; 6 | 7 | public interface IInventoryDomainService 8 | { 9 | Task CreateNewInventoryAsync(CreateInventoryDto createInventoryDto, CancellationToken cancellationToken); 10 | 11 | Task AddMorePpeAsync(AddPpeDto addPpeDto, CancellationToken cancellationToken); 12 | 13 | 14 | } -------------------------------------------------------------------------------- /src/Core/Application/Ports/DomainServices/IOperatorDomainService.cs: -------------------------------------------------------------------------------- 1 | using Application.DataTransferObject; 2 | using MongoDB.Bson; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Application.Ports.DomainServices; 8 | 9 | public interface IOperatorDomainService 10 | { 11 | Task RegisterOperatorAsync(RegisterOperatorDto registerWorkerDto, CancellationToken cancellationToken); 12 | 13 | Task RetrieveOperatorDetailsAsync(string operatorId, CancellationToken cancellationToken); 14 | 15 | Task> RetrieveOperatorsAsync(CancellationToken cancellationToken); 16 | } 17 | -------------------------------------------------------------------------------- /src/Core/Application/Ports/MongoServices/IInventoryService.cs: -------------------------------------------------------------------------------- 1 | using Domain.Aggregates.Inventory; 2 | using MongoDB.Bson; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Application.Ports.MongoServices; 8 | 9 | public interface IInventoryService 10 | { 11 | Task SaveNewInventoryAsync(Inventory inventory, CancellationToken cancellationToken); 12 | 13 | Task GetInventoryByIdAsync(ObjectId inventoryId, CancellationToken cancellationToken); 14 | 15 | Task> GetAllInventoryAsync(CancellationToken cancellationToken); 16 | 17 | Task UpdateInventoryAsync(Inventory inventory, CancellationToken cancellationToken); 18 | } -------------------------------------------------------------------------------- /src/Core/Application/Ports/MongoServices/IOperatorService.cs: -------------------------------------------------------------------------------- 1 | using Domain.Aggregates.Employee.Operator; 2 | using MongoDB.Bson; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Application.Ports.MongoServices; 8 | 9 | public interface IOperatorService 10 | { 11 | Task SaveNewOperatorAsync(Operator @operator, CancellationToken cancellationToken); 12 | 13 | Task UpdateOperatorAsync(Operator @operator, CancellationToken cancellationToken); 14 | 15 | Task GetOperatorByIdAsync(ObjectId operatorId, CancellationToken cancellationToken); 16 | 17 | Task> GetAllOperators(List fields, CancellationToken cancellationToken); 18 | } 19 | -------------------------------------------------------------------------------- /src/Core/Application/Ports/NotificationServices/INotificationContext.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using System.Collections.Generic; 3 | 4 | namespace Application.Ports.NotificationServices; 5 | 6 | public interface INotificationContext 7 | { 8 | IReadOnlyCollection Notifications { get; } 9 | 10 | bool HasNotifications { get; } 11 | 12 | void AddNotification(string errorMessage); 13 | 14 | void AddNotification(ValidationFailure validationFailure); 15 | 16 | void AddNotification(ValidationResult validationResult); 17 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Aggregates/AggregateRoot.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Entities; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Domain.Abstractions.Aggregates; 5 | 6 | public abstract class AggregateRoot : Entity, IAggregateRoot 7 | where TId : struct 8 | { 9 | [BsonId] 10 | public TId Id { get; protected set; } 11 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Aggregates/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace Domain.Abstractions.Aggregates; 2 | 3 | public interface IAggregateRoot 4 | where TId : struct 5 | { 6 | TId Id { get; } 7 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Entitites/Entity.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using FluentValidation.Results; 3 | using MongoDB.Bson.Serialization.Attributes; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Domain.Abstractions.Entities; 11 | 12 | public abstract class Entity : IEntity 13 | { 14 | [BsonIgnore] 15 | private ValidationResult _validationResult = new(); 16 | 17 | [BsonIgnore] 18 | public bool IsValid 19 | => Validate(); 20 | 21 | [BsonIgnore] 22 | public IEnumerable Errors 23 | => _validationResult.Errors; 24 | 25 | protected void AddErrors(IReadOnlyCollection failures) 26 | => _validationResult.Errors.AddRange(failures); 27 | 28 | public bool IsDeleted { get; protected set; } 29 | 30 | protected bool OnValidate() 31 | where TValidator : AbstractValidator, new() 32 | where TEntity : Entity 33 | { 34 | var validationResult = new TValidator().Validate(this as TEntity); 35 | _validationResult.Errors.AddRange(validationResult.Errors); 36 | 37 | return _validationResult.IsValid; 38 | } 39 | 40 | protected abstract bool Validate(); 41 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Entitites/IEntity.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Domain.Abstractions.Entities; 9 | 10 | public interface IEntity 11 | { 12 | bool IsDeleted { get; } 13 | 14 | bool IsValid { get; } 15 | 16 | public IEnumerable Errors { get; } 17 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Enumerations/Enumeration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Domain.Abstractions.Enumerations; 7 | 8 | public abstract record Enumeration : IComparable 9 | { 10 | public Enumeration(int id, string name) 11 | => (Id, Name) = (id, name); 12 | 13 | public int Id { get; private set; } 14 | 15 | public string Name { get; private set; } 16 | 17 | public override string ToString() 18 | => Name; 19 | 20 | public static IEnumerable GetAll() 21 | where T : Enumeration 22 | => typeof(T).GetFields( 23 | BindingFlags.Public | 24 | BindingFlags.Static | 25 | BindingFlags.DeclaredOnly) 26 | .Select(f => f.GetValue(null)).Cast(); 27 | 28 | public int CompareTo(object obj) 29 | => Id.CompareTo(((Enumeration)obj).Id); 30 | } -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/Validators/AggregateValidator.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Aggregates; 2 | using Domain.Abstractions.Entities; 3 | using FluentValidation; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Domain.Abstractions.Validators; 11 | 12 | public abstract class AggregateValidator : AbstractValidator 13 | where TAggregateRoot : IAggregateRoot 14 | where TId : struct 15 | { 16 | protected AggregateValidator() 17 | => RuleFor(entity => entity.Id) 18 | .NotEqual(default(TId)); 19 | } 20 | -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/ValueObjects/ValueObject.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using FluentValidation.Results; 3 | using MongoDB.Bson.Serialization.Attributes; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Domain.Abstractions.ValueObjects; 11 | 12 | public abstract record ValueObject 13 | { 14 | [BsonIgnore] 15 | private ValidationResult ValidationResult { get; set; } = new(); 16 | 17 | [BsonIgnore] 18 | public bool IsValid 19 | => Validate(); 20 | 21 | [BsonIgnore] 22 | public IEnumerable Errors 23 | => ValidationResult.Errors; 24 | 25 | protected bool OnValidate() 26 | where TValidator : AbstractValidator, new() 27 | where TValueObject : ValueObject 28 | { 29 | ValidationResult = new TValidator().Validate(this as TValueObject); 30 | return ValidationResult.IsValid; 31 | } 32 | 33 | protected abstract bool Validate(); 34 | } 35 | -------------------------------------------------------------------------------- /src/Core/Domain/Abstractions/ValueTypes/IValueType.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Domain.Abstractions.ValueTypes; 10 | 11 | public interface IValueType 12 | { 13 | [BsonIgnore] 14 | public bool IsValid { get; } 15 | 16 | [BsonIgnore] 17 | public IReadOnlyCollection Errors { get; } 18 | 19 | public TValue Value { get; } 20 | 21 | public void Create(object value); 22 | } 23 | -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/Employee.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Aggregates; 2 | using Domain.Serializers; 3 | using Domain.ValueTypes; 4 | using MongoDB.Bson; 5 | using MongoDB.Bson.Serialization.Attributes; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Domain.Aggregates.Employee; 10 | 11 | public abstract class Employee : AggregateRoot, IEmployee 12 | { 13 | public bool Active { get; protected set; } = true; 14 | 15 | [BsonSerializer(typeof(StructBsonSerializer))] 16 | public Name Name { get; init; } 17 | 18 | [BsonSerializer(typeof(StructBsonSerializer))] 19 | public Nin Nin { get; init; } 20 | 21 | [BsonSerializer(typeof(StructBsonSerializer))] 22 | public Password Password { get; init; } 23 | 24 | public DateTime BirthDate { get; init; } 25 | 26 | public DateTime AdmissionDate { get; private set; } = DateTime.Now; 27 | 28 | public DateTime? TerminationDate { get; protected set; } 29 | 30 | public virtual void TurnOffEmployee() 31 | { 32 | Active = false; 33 | TerminationDate = DateTime.Now; 34 | } 35 | 36 | protected override bool Validate() 37 | { 38 | AddErrors(Name.Errors); 39 | AddErrors(Nin.Errors); 40 | AddErrors(Password.Errors); 41 | 42 | return OnValidate(); 43 | } 44 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/EmployeeValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System; 3 | 4 | namespace Domain.Aggregates.Employee; 5 | 6 | public class EmployeeValidator : AbstractValidator 7 | { 8 | public EmployeeValidator() 9 | { 10 | RuleFor(employee => employee.BirthDate) 11 | .GreaterThan(new DateTime(1900, 12, 1)) 12 | .WithMessage("You are very old!") 13 | .LessThan(DateTime.Now.AddYears(-8)) 14 | .WithMessage("You are very young!"); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/IEmployee.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Aggregates; 2 | using MongoDB.Bson; 3 | using System; 4 | 5 | namespace Domain.Aggregates.Employee; 6 | 7 | public interface IEmployee : IAggregateRoot { } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/Manager/Manager.cs: -------------------------------------------------------------------------------- 1 | namespace Domain.Aggregates.Employee.Manager; 2 | 3 | public class Manager : Employee 4 | { 5 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/Manager/ManagerValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Domain.Aggregates.Employee.Manager; 4 | 5 | public class ManagerValidator : AbstractValidator 6 | { 7 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/Operator/Operator.cs: -------------------------------------------------------------------------------- 1 | using Domain.Enumerations; 2 | using Domain.ValueObjects.Ppes; 3 | using MongoDB.Bson; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Domain.Aggregates.Employee.Operator; 9 | 10 | public class Operator : Employee 11 | { 12 | public WorkShift WorkShift { get; private set; } 13 | 14 | public ICollection Ppes { get; private set; } 15 | 16 | public void ChangeShift(WorkShift workShift) 17 | => WorkShift = workShift; 18 | 19 | public void AddPpes(List epis) 20 | => epis.ForEach(epi => Ppes.Add(epi)); 21 | 22 | public IReadOnlyCollection RemoveExpiredPpes() 23 | { 24 | var experied = Ppes.Where(epi => epi.Expiration < DateOnly.FromDateTime(DateTime.Now)); 25 | 26 | Ppes = Ppes.Where(epi => epi.Expiration >= DateOnly.FromDateTime(DateTime.Now)).ToList(); 27 | 28 | return experied.ToList(); 29 | } 30 | 31 | public Ppe ReturnPpe(ObjectId epiId) 32 | { 33 | var epi = Ppes.First(epi => epi.InventoryId.Equals(epiId)); 34 | 35 | Ppes.Remove(epi); 36 | 37 | return epi; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Employee/Operator/OperatorValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Domain.Aggregates.Employee.Operator 9 | { 10 | public class OperatorValidator : AbstractValidator 11 | { 12 | public OperatorValidator() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Inventory/Inventory.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Aggregates; 2 | using Domain.Entities.Ppe; 3 | using System; 4 | 5 | namespace Domain.Aggregates.Inventory; 6 | 7 | public class Inventory : AggregateRoot 8 | { 9 | public Ppe Ppe { get; init; } 10 | 11 | public long AvailableQuantity { get; private set; } 12 | 13 | public long DepreciatedQuantity { get; private set; } 14 | 15 | public long TotalAmount { get; private set; } 16 | 17 | public void AddAvailableQuantity(long quantity = 1) 18 | { 19 | AvailableQuantity += quantity; 20 | UpdateTotalAmount(); 21 | } 22 | 23 | public void AddDepreciatedQuantity(long quantity = 1) 24 | { 25 | AvailableQuantity -= quantity; 26 | DepreciatedQuantity += quantity; 27 | UpdateTotalAmount(); 28 | } 29 | 30 | private void UpdateTotalAmount() 31 | => TotalAmount = AvailableQuantity + DepreciatedQuantity; 32 | 33 | protected override bool Validate() 34 | => OnValidate(); 35 | } -------------------------------------------------------------------------------- /src/Core/Domain/Aggregates/Inventory/InventoryValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Domain.Aggregates.Inventory; 4 | 5 | public class InventoryValidator : AbstractValidator 6 | { 7 | } -------------------------------------------------------------------------------- /src/Core/Domain/Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Core/Domain/Entities/Ppe/Ppe.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Entities; 2 | using System; 3 | 4 | namespace Domain.Entities.Ppe; 5 | 6 | public class Ppe : Entity 7 | { 8 | public string Name { get; init; } 9 | 10 | public string Description { get; init; } 11 | 12 | public DateOnly ManufacturingDate { get; init; } 13 | 14 | public long Durability { get; init; } 15 | 16 | public string ApprovalCertificate { get; init; } 17 | 18 | protected override bool Validate() 19 | => OnValidate(); 20 | } 21 | -------------------------------------------------------------------------------- /src/Core/Domain/Entities/Ppe/PpeValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Domain.Entities.Ppe; 4 | 5 | public class PpeValidator : AbstractValidator 6 | { 7 | public PpeValidator() 8 | { 9 | RuleFor(ppe => ppe.Name) 10 | .NotEmpty() 11 | .NotNull() 12 | .WithMessage("Inform a valid name."); 13 | 14 | RuleFor(ppe => ppe.Name.Length) 15 | .GreaterThan(3) 16 | .WithMessage("Inform a valid PPE name."); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Core/Domain/Enumerations/WorkShift.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.Enumerations; 2 | 3 | namespace Domain.Enumerations; 4 | 5 | public record WorkShift : Enumeration 6 | { 7 | public readonly static WorkShift NightShift = new(1, nameof(NightShift)); 8 | public readonly static WorkShift MorningShift = new(2, nameof(MorningShift)); 9 | public readonly static WorkShift DawnShift = new(3, nameof(DawnShift)); 10 | 11 | private WorkShift(int id, string name) 12 | : base(id, name) { } 13 | } 14 | -------------------------------------------------------------------------------- /src/Core/Domain/Serializers/StructBsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.ValueTypes; 2 | using Domain.ValueTypes; 3 | using MongoDB.Bson; 4 | using MongoDB.Bson.IO; 5 | using MongoDB.Bson.Serialization; 6 | using MongoDB.Bson.Serialization.Conventions; 7 | using MongoDB.Bson.Serialization.Serializers; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.ComponentModel; 11 | using System.Linq; 12 | using System.Reflection; 13 | using System.Runtime.CompilerServices; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | 17 | namespace Domain.Serializers 18 | { 19 | public class StructBsonSerializer : IBsonSerializer 20 | where T : struct 21 | { 22 | public Type ValueType { get => typeof(T); } 23 | 24 | public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 25 | { 26 | var bsonReader = context.Reader; 27 | bsonReader.ReadStartDocument(); 28 | var value = bsonReader.ReadString(); 29 | bsonReader.ReadEndDocument(); 30 | 31 | IValueType obj = new T() as IValueType; 32 | obj.Create(value); 33 | 34 | return obj; 35 | } 36 | 37 | public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) 38 | { 39 | var newVaule = value as IValueType; 40 | 41 | if(newVaule is not null) 42 | { 43 | context.Writer.WriteStartDocument(); 44 | context.Writer.WriteString(newVaule.GetType().Name.ToLowerInvariant(), newVaule.Value); 45 | context.Writer.WriteEndDocument(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Core/Domain/ValueObjects/Ppes/Ppe.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.ValueObjects; 2 | using MongoDB.Bson; 3 | using System; 4 | 5 | namespace Domain.ValueObjects.Ppes; 6 | 7 | public record Ppe : ValueObject 8 | { 9 | public ObjectId InventoryId { get; init; } 10 | 11 | public DateOnly Expiration { get; init; } 12 | 13 | protected override bool Validate() 14 | => OnValidate(); 15 | } -------------------------------------------------------------------------------- /src/Core/Domain/ValueObjects/Ppes/PpeValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System; 3 | 4 | namespace Domain.ValueObjects.Ppes; 5 | 6 | public class PpeValidator : AbstractValidator 7 | { 8 | public PpeValidator() 9 | { 10 | RuleFor(epi => epi.InventoryId) 11 | .NotEmpty().NotNull() 12 | .WithMessage("Inform a valid EPI!"); 13 | 14 | When(epi => epi.InventoryId != default, () => 15 | { 16 | RuleFor(epi => epi.Expiration) 17 | .NotNull().NotEmpty().LessThanOrEqualTo(DateOnly.FromDateTime(DateTime.Now)) 18 | .WithMessage(epi => $"Inform a valid Expiration Date for EPI {epi.InventoryId}"); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Core/Domain/ValueTypes/Name.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.ValueTypes; 2 | using FluentValidation.Results; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace Domain.ValueTypes; 9 | 10 | public struct Name : IValueType 11 | { 12 | private string _name; 13 | private readonly List _errors; 14 | 15 | private Name(string name) 16 | : this() 17 | { 18 | _name = name; 19 | _errors = new List(); 20 | Validate(); 21 | } 22 | 23 | public bool IsValid => Errors?.Any() is false; 24 | 25 | public IReadOnlyCollection Errors { get; private set; } 26 | 27 | public string Value => _name; 28 | 29 | public override string ToString() => 30 | _name; 31 | 32 | public static implicit operator Name(string name) => 33 | new Name(name); 34 | 35 | private void Validate() 36 | { 37 | if (string.IsNullOrWhiteSpace(_name)) 38 | _errors.Add(new ValidationFailure(GetType().Name, "Inform a valid name.")); 39 | 40 | if (_name.Length < 10) 41 | _errors.Add(new ValidationFailure(GetType().Name, "The name must have more than 10 chars.")); 42 | 43 | if (!Regex.IsMatch(_name, (@"[^a-zA-Z0-9]"))) 44 | _errors.Add(new ValidationFailure(GetType().Name, "The name must not have any special char.")); 45 | 46 | Errors = _errors; 47 | } 48 | 49 | public void Create(object value) 50 | { 51 | _name = value.ToString(); 52 | Validate(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Core/Domain/ValueTypes/Nin.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.ValueTypes; 2 | using FluentValidation.Results; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Domain.ValueTypes; 7 | 8 | public struct Nin : IValueType 9 | { 10 | private string _nin; 11 | private readonly List _errors; 12 | 13 | private Nin(string nin) 14 | : this() 15 | { 16 | _nin = nin; 17 | _errors = new List(); 18 | Validate(); 19 | } 20 | 21 | public bool IsValid { get => Errors?.Any() ?? true; } 22 | 23 | public IReadOnlyCollection Errors { get; private set; } 24 | 25 | public string Value => _nin; 26 | 27 | public override string ToString() => 28 | _nin; 29 | 30 | public static implicit operator Nin(string nin) => 31 | new Nin(nin); 32 | 33 | private void Validate() 34 | { 35 | if (string.IsNullOrWhiteSpace(_nin)) 36 | _errors.Add(new ValidationFailure(GetType().Name, "Is necessary to inform the CPF.")); 37 | 38 | int[] multiplierOne = new int[9] { 10, 9, 8, 7, 6, 5, 4, 3, 2 }; 39 | int[] multiplierTwo = new int[10] { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 }; 40 | string aux; 41 | string digit; 42 | int sum, rest; 43 | 44 | var value = _nin.Trim(); 45 | value = _nin.Replace(".", "").Replace("-", ""); 46 | 47 | if (value.Length != 11) 48 | _errors.Add(new ValidationFailure(GetType().Name, "CPF should have 11 chars.")); 49 | 50 | aux = value.Substring(0, 9); 51 | sum = 0; 52 | 53 | for (int i = 0; i < 9; i++) 54 | sum += int.Parse(aux[i].ToString()) * multiplierOne[i]; 55 | 56 | rest = sum % 11; 57 | 58 | if (rest < 2) 59 | rest = 0; 60 | else 61 | rest = 11 - rest; 62 | 63 | digit = rest.ToString(); 64 | aux = aux + digit; 65 | sum = 0; 66 | 67 | for (int i = 0; i < 10; i++) 68 | sum += int.Parse(aux[i].ToString()) * multiplierTwo[i]; 69 | 70 | rest = sum % 11; 71 | 72 | if (rest < 2) 73 | rest = 0; 74 | else 75 | rest = 11 - rest; 76 | 77 | digit = digit + rest.ToString(); 78 | 79 | if (!value.EndsWith(digit)) 80 | _errors.Add(new ValidationFailure(GetType().Name, "This CPF is invalid.")); 81 | 82 | Errors = _errors; 83 | } 84 | 85 | public void Create(object value) 86 | { 87 | _nin = value.ToString(); 88 | Validate(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Core/Domain/ValueTypes/Password.cs: -------------------------------------------------------------------------------- 1 | using Domain.Abstractions.ValueTypes; 2 | using FluentValidation.Results; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace Domain.ValueTypes; 8 | 9 | public struct Password : IValueType 10 | { 11 | private string _password; 12 | private readonly List _errors; 13 | 14 | private Password(string password) 15 | : this() 16 | { 17 | _password = password; 18 | _errors = new List(); 19 | Validate(); 20 | } 21 | 22 | public bool IsValid => Errors?.Any() is false; 23 | 24 | public IReadOnlyCollection Errors { get; private set; } 25 | 26 | public string Value => _password; 27 | 28 | public override string ToString() => 29 | _password; 30 | 31 | public static implicit operator Password(string password) => 32 | new Password(password); 33 | 34 | private void Validate() 35 | { 36 | if (string.IsNullOrWhiteSpace(_password)) 37 | _errors.Add(new ValidationFailure(GetType().Name, "Is necessary to inform the Password.")); 38 | 39 | if (_password.Length < 6) 40 | _errors.Add(new ValidationFailure(GetType().Name, "The password must have more than 6 chars.")); 41 | 42 | if (Regex.IsMatch(_password, (@"[^a-zA-Z0-9]"))) 43 | _errors.Add(new ValidationFailure(GetType().Name, "The password must not have any special char.")); 44 | 45 | Errors = _errors; 46 | } 47 | 48 | public void Create(object value) 49 | { 50 | _password = value.ToString(); 51 | Validate(); 52 | } 53 | } --------------------------------------------------------------------------------