├── .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 | 
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 | 
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 | }
--------------------------------------------------------------------------------