├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── README.md
├── docker-compose.yml
├── dotnet-core-kafka.sln
├── img
├── customer_db.png
├── identity_db.png
└── kafdrop.JPG
└── src
├── Services.Customer
├── Controllers
│ └── CustomersController.cs
├── Data
│ ├── CustomerDBContext.cs
│ ├── DbInitilializer.cs
│ ├── Entity
│ │ └── Customer.cs
│ └── Migrations
│ │ ├── 20200910204651_initial.Designer.cs
│ │ ├── 20200910204651_initial.cs
│ │ └── CustomerDBContextModelSnapshot.cs
├── Events
│ └── Handlers
│ │ └── UserCreatedHandler.cs
├── Messages
│ └── User.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── README.md
├── Services.Customer.csproj
├── Startup.cs
├── appsettings.Development.json
└── appsettings.json
├── Services.Identity
├── Commands
│ ├── Handlers
│ │ └── RegisterUserCommandHandler.cs
│ └── RegisterUserCommand.cs
├── Controllers
│ └── AccountsController.cs
├── Data
│ ├── DbInitilializer.cs
│ ├── Entity
│ │ └── User.cs
│ ├── IdentityDBContext.cs
│ └── Migrations
│ │ ├── 20200910205638_initial.Designer.cs
│ │ ├── 20200910205638_initial.cs
│ │ └── IdentityDBContextModelSnapshot.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── README.md
├── Services.Identity.csproj
├── Startup.cs
├── appsettings.Development.json
└── appsettings.json
└── Shared
├── Kafka
├── Consumer
│ ├── BackGroundKafkaConsumer.cs
│ ├── IKafkaHandler.cs
│ └── KafkaConsumerConfig.cs
├── IKafkaMessageBus.cs
├── KafkaDeserializer.cs
├── KafkaMessageBus.cs
├── KafkaSerializer.cs
├── Producer
│ ├── KafkaProducer.cs
│ └── KafkaProducerConfig.cs
└── RegisterServiceExtensions.cs
└── Shared.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # Visual Studio code coverage results
114 | *.coverage
115 | *.coveragexml
116 |
117 | # NCrunch
118 | _NCrunch_*
119 | .*crunch*.local.xml
120 | nCrunchTemp_*
121 |
122 | # MightyMoose
123 | *.mm.*
124 | AutoTest.Net/
125 |
126 | # Web workbench (sass)
127 | .sass-cache/
128 |
129 | # Installshield output folder
130 | [Ee]xpress/
131 |
132 | # DocProject is a documentation generator add-in
133 | DocProject/buildhelp/
134 | DocProject/Help/*.HxT
135 | DocProject/Help/*.HxC
136 | DocProject/Help/*.hhc
137 | DocProject/Help/*.hhk
138 | DocProject/Help/*.hhp
139 | DocProject/Help/Html2
140 | DocProject/Help/html
141 |
142 | # Click-Once directory
143 | publish/
144 |
145 | # Publish Web Output
146 | *.[Pp]ublish.xml
147 | *.azurePubxml
148 | # TODO: Comment the next line if you want to checkin your web deploy settings
149 | # but database connection strings (with potential passwords) will be unencrypted
150 | *.pubxml
151 | *.publishproj
152 |
153 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
154 | # checkin your Azure Web App publish settings, but sensitive information contained
155 | # in these scripts will be unencrypted
156 | PublishScripts/
157 |
158 | # NuGet Packages
159 | *.nupkg
160 | # The packages folder can be ignored because of Package Restore
161 | **/packages/*
162 | # except build/, which is used as an MSBuild target.
163 | !**/packages/build/
164 | # Uncomment if necessary however generally it will be regenerated when needed
165 | #!**/packages/repositories.config
166 | # NuGet v3's project.json files produces more ignoreable files
167 | *.nuget.props
168 | *.nuget.targets
169 |
170 | # Microsoft Azure Build Output
171 | csx/
172 | *.build.csdef
173 |
174 | # Microsoft Azure Emulator
175 | ecf/
176 | rcf/
177 |
178 | # Windows Store app package directories and files
179 | AppPackages/
180 | BundleArtifacts/
181 | Package.StoreAssociation.xml
182 | _pkginfo.txt
183 |
184 | # Visual Studio cache files
185 | # files ending in .cache can be ignored
186 | *.[Cc]ache
187 | # but keep track of directories ending in .cache
188 | !*.[Cc]ache/
189 |
190 | # Others
191 | ClientBin/
192 | ~$*
193 | *~
194 | *.dbmdl
195 | *.dbproj.schemaview
196 | *.jfm
197 | *.pfx
198 | *.publishsettings
199 | node_modules/
200 | orleans.codegen.cs
201 |
202 | # Since there are multiple workflows, uncomment next line to ignore bower_components
203 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
204 | #bower_components/
205 |
206 | # RIA/Silverlight projects
207 | Generated_Code/
208 |
209 | # Backup & report files from converting an old project file
210 | # to a newer Visual Studio version. Backup files are not needed,
211 | # because we have git ;-)
212 | _UpgradeReport_Files/
213 | Backup*/
214 | UpgradeLog*.XML
215 | UpgradeLog*.htm
216 |
217 | # SQL Server files
218 | *.mdf
219 | *.ldf
220 |
221 | # Grafana
222 | *.db
223 |
224 |
225 | # Business Intelligence projects
226 | *.rdl.data
227 | *.bim.layout
228 | *.bim_*.settings
229 |
230 | # Microsoft Fakes
231 | FakesAssemblies/
232 |
233 | # GhostDoc plugin setting file
234 | *.GhostDoc.xml
235 |
236 | # Node.js Tools for Visual Studio
237 | .ntvs_analysis.dat
238 |
239 | # Visual Studio 6 build log
240 | *.plg
241 |
242 | # Visual Studio 6 workspace options file
243 | *.opt
244 |
245 | # Visual Studio LightSwitch build output
246 | **/*.HTMLClient/GeneratedArtifacts
247 | **/*.DesktopClient/GeneratedArtifacts
248 | **/*.DesktopClient/ModelManifest.xml
249 | **/*.Server/GeneratedArtifacts
250 | **/*.Server/ModelManifest.xml
251 | _Pvt_Extensions
252 |
253 | # Paket dependency manager
254 | .paket/paket.exe
255 | paket-files/
256 |
257 | # FAKE - F# Make
258 | .fake/
259 |
260 | # JetBrains Rider
261 | .idea/
262 | *.sln.iml
263 |
264 | # CodeRush
265 | .cr/
266 |
267 | # Python Tools for Visual Studio (PTVS)
268 | __pycache__/
269 | *.pyc
270 |
271 | .vscode/*
272 | !.vscode/settings.json
273 | !.vscode/tasks.json
274 | !.vscode/launch.json
275 |
276 | NuGet.config
277 | appsettings.local.json
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "identity",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build-identity",
12 | "program": "${workspaceFolder}/src/Services.Identity/bin/Debug/netcoreapp3.1/Services.Identity.dll",
13 | "args": [],
14 | "cwd": "${workspaceFolder}/src/Services.Identity",
15 | "stopAtEntry": false,
16 | "internalConsoleOptions": "openOnSessionStart",
17 | "launchBrowser": {
18 | "enabled": true,
19 | "args": "${auto-detect-url}",
20 | "windows": {
21 | "command": "cmd.exe",
22 | "args": "/C start ${auto-detect-url}"
23 | },
24 | "osx": {
25 | "command": "open"
26 | },
27 | "linux": {
28 | "command": "xdg-open"
29 | }
30 | },
31 | "env": {
32 | "ASPNETCORE_ENVIRONMENT": "Development",
33 | "ASPNETCORE_URLS": "http://localhost:5001"
34 | },
35 | "sourceFileMap": {
36 | "/Views": "${workspaceFolder}/Views"
37 | }
38 | },
39 | {
40 | "name": "customer",
41 | "type": "coreclr",
42 | "request": "launch",
43 | "preLaunchTask": "build-customer",
44 | "program": "${workspaceFolder}/src/Services.Customer/bin/Debug/netcoreapp3.1/Services.Customer.dll",
45 | "args": [],
46 | "cwd": "${workspaceFolder}/src/Services.Customer",
47 | "stopAtEntry": false,
48 | "internalConsoleOptions": "openOnSessionStart",
49 | "launchBrowser": {
50 | "enabled": true,
51 | "args": "${auto-detect-url}",
52 | "windows": {
53 | "command": "cmd.exe",
54 | "args": "/C start ${auto-detect-url}"
55 | },
56 | "osx": {
57 | "command": "open"
58 | },
59 | "linux": {
60 | "command": "xdg-open"
61 | }
62 | },
63 | "env": {
64 | "ASPNETCORE_ENVIRONMENT": "Development",
65 | "ASPNETCORE_URLS": "http://localhost:5005"
66 | },
67 | "sourceFileMap": {
68 | "/Views": "${workspaceFolder}/Views"
69 | }
70 | },
71 | {
72 | "name": ".NET Core Attach",
73 | "type": "coreclr",
74 | "request": "attach",
75 | "processId": "${command:pickProcess}"
76 | }
77 | ],
78 | "compounds": [
79 | {
80 | "name": "All",
81 | "configurations": [
82 | "identity",
83 | "customer"
84 | ]
85 | }
86 | ]
87 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build-identity",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/src/Services.Identity/Services.Identity.csproj"
11 | ],
12 | "problemMatcher": "$msCompile"
13 | },
14 | {
15 | "label": "build-customer",
16 | "command": "dotnet",
17 | "type": "process",
18 | "args": [
19 | "build",
20 | "${workspaceFolder}/src/Services.Customer/Services.Customer.csproj",
21 | ],
22 | "problemMatcher": "$msCompile"
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Asp dot Net Core microservices that communicate asynchronous through Kafka message broker.
2 |
3 | ## Prerequities
4 |
5 | * DotNet Core SDK 3.1
6 | * Docker
7 | * pgAdmin or Azure Data Studio
8 |
9 |
10 | ## Running in Debug Mode
11 |
12 | * Run 'docker-compose up'
13 | * Wait all infra to up and running.
14 | * Select 'All' debug option and start debuging. ( for vscode)
15 | * Wait until all microservices are up and running.
16 |
17 | ## How Can I Test?
18 |
19 | * Create a user in the identity service with the following json.
20 |
21 | Endpoint: http://localhost:5001/api/sign-up
22 |
23 | ```json
24 | {
25 | "FirstName" : "Suat",
26 | "LastName" : "Köse",
27 | "Address" : "Üsküdar",
28 | "Email" : "email@gmail.com",
29 | "Password" : "12345"
30 | }
31 | ```
32 |
33 | * Check Identity and ```Customer``` databases to make sure user and customer are created properly.
34 |
35 |
36 |
37 |
38 |
39 | * Check KafDrop (http://localhost:9000) to see messages on the ```users``` topic.
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 |
5 | postgres:
6 | image: postgres:10.6-alpine
7 | container_name: kafka_dotnet_postgres
8 | networks:
9 | - broker-kafka
10 | ports:
11 | - "5499:5432"
12 | environment:
13 | - POSTGRES_PASSWORD=postgres
14 |
15 | zookeeper:
16 | image: confluentinc/cp-zookeeper:latest
17 | container_name: zookeeper
18 | networks:
19 | - broker-kafka
20 | ports:
21 | - 2181:2181
22 | environment:
23 | ZOOKEEPER_CLIENT_PORT: 2181
24 | ZOOKEEPER_TICK_TIME: 2000
25 |
26 | kafka:
27 | image: confluentinc/cp-kafka:latest
28 | container_name: kafka
29 | networks:
30 | - broker-kafka
31 | depends_on:
32 | - zookeeper
33 | ports:
34 | - 9092:9092
35 | environment:
36 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
37 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
38 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
39 | KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
40 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
41 | KAFKA_LOG_CLEANER_DELETE_RETENTION_MS: 5000
42 | KAFKA_BROKER_ID: 1
43 | KAFKA_MIN_INSYNC_REPLICAS: 1
44 |
45 | kafdrop:
46 | image: obsidiandynamics/kafdrop:latest
47 | container_name: kafdrop
48 | networks:
49 | - broker-kafka
50 | depends_on:
51 | - kafka
52 | ports:
53 | - 9000:9000
54 | environment:
55 | KAFKA_BROKERCONNECT: kafka:29092
56 |
57 | networks:
58 | broker-kafka:
59 | driver: bridge
--------------------------------------------------------------------------------
/dotnet-core-kafka.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{26EFD2CC-DD20-4083-9A74-A4582AA43ECD}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services.Customer", "src\Services.Customer\Services.Customer.csproj", "{FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services.Identity", "src\Services.Identity\Services.Identity.csproj", "{52D976B2-BFD6-4746-AA80-ADF03E876255}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Debug|x64 = Debug|x64
18 | Debug|x86 = Debug|x86
19 | Release|Any CPU = Release|Any CPU
20 | Release|x64 = Release|x64
21 | Release|x86 = Release|x86
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|x64.ActiveCfg = Debug|Any CPU
30 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|x64.Build.0 = Debug|Any CPU
31 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|x86.ActiveCfg = Debug|Any CPU
32 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Debug|x86.Build.0 = Debug|Any CPU
33 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|x64.ActiveCfg = Release|Any CPU
36 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|x64.Build.0 = Release|Any CPU
37 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|x86.ActiveCfg = Release|Any CPU
38 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B}.Release|x86.Build.0 = Release|Any CPU
39 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|x64.ActiveCfg = Debug|Any CPU
42 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|x64.Build.0 = Debug|Any CPU
43 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|x86.ActiveCfg = Debug|Any CPU
44 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Debug|x86.Build.0 = Debug|Any CPU
45 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|x64.ActiveCfg = Release|Any CPU
48 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|x64.Build.0 = Release|Any CPU
49 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|x86.ActiveCfg = Release|Any CPU
50 | {52D976B2-BFD6-4746-AA80-ADF03E876255}.Release|x86.Build.0 = Release|Any CPU
51 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
53 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|x64.ActiveCfg = Debug|Any CPU
54 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|x64.Build.0 = Debug|Any CPU
55 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|x86.ActiveCfg = Debug|Any CPU
56 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Debug|x86.Build.0 = Debug|Any CPU
57 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|x64.ActiveCfg = Release|Any CPU
60 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|x64.Build.0 = Release|Any CPU
61 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|x86.ActiveCfg = Release|Any CPU
62 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5}.Release|x86.Build.0 = Release|Any CPU
63 | EndGlobalSection
64 | GlobalSection(NestedProjects) = preSolution
65 | {FD43FD79-E6D5-49B5-A9A3-3F0B9A49C11B} = {26EFD2CC-DD20-4083-9A74-A4582AA43ECD}
66 | {52D976B2-BFD6-4746-AA80-ADF03E876255} = {26EFD2CC-DD20-4083-9A74-A4582AA43ECD}
67 | {FD81B39B-C0BE-4668-A78E-B8B5ADDC95D5} = {26EFD2CC-DD20-4083-9A74-A4582AA43ECD}
68 | EndGlobalSection
69 | EndGlobal
70 |
--------------------------------------------------------------------------------
/img/customer_db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suadev/dotnet-core-microservices-kafka/b190fad3ecde72e5ed926ad516441bf3b5b05d8e/img/customer_db.png
--------------------------------------------------------------------------------
/img/identity_db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suadev/dotnet-core-microservices-kafka/b190fad3ecde72e5ed926ad516441bf3b5b05d8e/img/identity_db.png
--------------------------------------------------------------------------------
/img/kafdrop.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suadev/dotnet-core-microservices-kafka/b190fad3ecde72e5ed926ad516441bf3b5b05d8e/img/kafdrop.JPG
--------------------------------------------------------------------------------
/src/Services.Customer/Controllers/CustomersController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.EntityFrameworkCore;
5 | using Services.Customer.Data;
6 |
7 | namespace Services.Customer.Controllers
8 | {
9 | [Route("api/customers")]
10 | [ApiController]
11 | public class CustomersController : ControllerBase
12 | {
13 | private readonly CustomerDBContext _dbContext;
14 |
15 | public CustomersController(CustomerDBContext dbContext)
16 | {
17 | _dbContext = dbContext;
18 | }
19 |
20 | [HttpGet("{id}")]
21 | public async Task Get(Guid id)
22 | {
23 | return Ok(await _dbContext.Customers.FindAsync(id));
24 | }
25 |
26 | [HttpGet]
27 | public async Task Get()
28 | {
29 | return Ok(await _dbContext.Customers.ToListAsync());
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Data/CustomerDBContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace Services.Customer.Data
4 | {
5 | public class CustomerDBContext : DbContext
6 | {
7 | public CustomerDBContext(DbContextOptions options) : base(options)
8 | {
9 | }
10 | public DbSet Customers { get; set; }
11 |
12 | protected override void OnModelCreating(ModelBuilder modelBuilder)
13 | {
14 | modelBuilder.HasDefaultSchema("customer");
15 | modelBuilder.Entity().ToTable("customers");
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Data/DbInitilializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace Services.Customer.Data
6 | {
7 | public static class DbInitilializer
8 | {
9 | public static void Initialize(IServiceProvider serviceProvider)
10 | {
11 | using (var scope = serviceProvider.CreateScope())
12 | {
13 | var context = scope.ServiceProvider.GetRequiredService();
14 | context.Database.Migrate();
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Data/Entity/Customer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Services.Customer.Data
4 | {
5 | public class Customer
6 | {
7 | public Guid Id { get; set; }
8 | public string Email { get; set; }
9 | public string FirstName { get; set; }
10 | public string LastName { get; set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Data/Migrations/20200910204651_initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
8 | using Services.Customer.Data;
9 |
10 | namespace Services.Customer.Data.Migrations
11 | {
12 | [DbContext(typeof(CustomerDBContext))]
13 | [Migration("20200910204651_initial")]
14 | partial class initial
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasDefaultSchema("customer")
21 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
22 | .HasAnnotation("ProductVersion", "3.1.2")
23 | .HasAnnotation("Relational:MaxIdentifierLength", 63);
24 |
25 | modelBuilder.Entity("Services.Customer.Data.Customer", b =>
26 | {
27 | b.Property("Id")
28 | .ValueGeneratedOnAdd()
29 | .HasColumnType("uuid");
30 |
31 | b.Property("Email")
32 | .HasColumnType("text");
33 |
34 | b.Property("FirstName")
35 | .HasColumnType("text");
36 |
37 | b.Property("LastName")
38 | .HasColumnType("text");
39 |
40 | b.HasKey("Id");
41 |
42 | b.ToTable("customers");
43 | });
44 | #pragma warning restore 612, 618
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Services.Customer/Data/Migrations/20200910204651_initial.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | namespace Services.Customer.Data.Migrations
5 | {
6 | public partial class initial : Migration
7 | {
8 | protected override void Up(MigrationBuilder migrationBuilder)
9 | {
10 | migrationBuilder.EnsureSchema(
11 | name: "customer");
12 |
13 | migrationBuilder.CreateTable(
14 | name: "customers",
15 | schema: "customer",
16 | columns: table => new
17 | {
18 | Id = table.Column(nullable: false),
19 | Email = table.Column(nullable: true),
20 | FirstName = table.Column(nullable: true),
21 | LastName = table.Column(nullable: true)
22 | },
23 | constraints: table =>
24 | {
25 | table.PrimaryKey("PK_customers", x => x.Id);
26 | });
27 | }
28 |
29 | protected override void Down(MigrationBuilder migrationBuilder)
30 | {
31 | migrationBuilder.DropTable(
32 | name: "customers",
33 | schema: "customer");
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Services.Customer/Data/Migrations/CustomerDBContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
6 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 | using Services.Customer.Data;
8 |
9 | namespace Services.Customer.Data.Migrations
10 | {
11 | [DbContext(typeof(CustomerDBContext))]
12 | partial class CustomerDBContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasDefaultSchema("customer")
19 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
20 | .HasAnnotation("ProductVersion", "3.1.2")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 63);
22 |
23 | modelBuilder.Entity("Services.Customer.Data.Customer", b =>
24 | {
25 | b.Property("Id")
26 | .ValueGeneratedOnAdd()
27 | .HasColumnType("uuid");
28 |
29 | b.Property("Email")
30 | .HasColumnType("text");
31 |
32 | b.Property("FirstName")
33 | .HasColumnType("text");
34 |
35 | b.Property("LastName")
36 | .HasColumnType("text");
37 |
38 | b.HasKey("Id");
39 |
40 | b.ToTable("customers");
41 | });
42 | #pragma warning restore 612, 618
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Services.Customer/Events/Handlers/UserCreatedHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Services.Customer.Data;
3 | using Services.Customer.Messages;
4 | using Shared.Kafka.Consumer;
5 |
6 | namespace Services.Customer.Handlers
7 | {
8 | public class UserCreatedHandler : IKafkaHandler
9 | {
10 | private readonly CustomerDBContext _dbContext;
11 |
12 | public UserCreatedHandler(CustomerDBContext dbContext)
13 | {
14 | _dbContext = dbContext;
15 | }
16 |
17 | public async Task HandleAsync(string key, User value)
18 | {
19 | _dbContext.Customers.Add(new Customer.Data.Customer
20 | {
21 | Id = value.Id,
22 | Email = value.Email,
23 | FirstName = value.FirstName,
24 | LastName = value.LastName,
25 | });
26 |
27 | await _dbContext.SaveChangesAsync();
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Messages/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Services.Customer.Messages
4 | {
5 | public class User
6 | {
7 | public Guid Id { get; set; }
8 | public string Email { get; set; }
9 | public string Password { get; set; }
10 | public string FirstName { get; set; }
11 | public string LastName { get; set; }
12 | public string Address { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Services.Customer/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Services.Customer
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Services.Customer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:5834",
8 | "sslPort": 44386
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "src": {
20 | "commandName": "Project",
21 | "launchBrowser": true,
22 | "applicationUrl": "http://localhost:5005",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Services.Customer/README.md:
--------------------------------------------------------------------------------
1 | **Migration**
2 |
3 | 0- cd Services.Customer
4 |
5 | 1- *dotnet ef migrations add "migration_name" -o ./Data/Migrations*
6 |
7 | 2- *dotnet ef database update*
--------------------------------------------------------------------------------
/src/Services.Customer/Services.Customer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | InProcess
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 | all
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Services.Customer/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using Services.Customer.Data;
8 | using Services.Customer.Handlers;
9 | using Services.Customer.Messages;
10 | using Shared.Kafka;
11 |
12 | namespace Services.Customer
13 | {
14 | public class Startup
15 | {
16 | public Startup(IConfiguration configuration)
17 | {
18 | Configuration = configuration;
19 | }
20 |
21 | public IConfiguration Configuration { get; }
22 |
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.AddDbContext(options =>
26 | options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
27 | );
28 |
29 | services.AddControllers();
30 |
31 | services.AddKafkaConsumer(p =>
32 | {
33 | p.Topic = "users";
34 | p.GroupId = "users_group";
35 | p.BootstrapServers = "localhost:9092";
36 | });
37 | }
38 |
39 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
40 | {
41 | if (env.IsDevelopment())
42 | {
43 | app.UseDeveloperExceptionPage();
44 | }
45 |
46 | DbInitilializer.Initialize(app.ApplicationServices);
47 |
48 | app.UseRouting();
49 | app.UseAuthorization();
50 | app.UseEndpoints(endpoints =>
51 | {
52 | endpoints.MapControllers();
53 | });
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Services.Customer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Services.Customer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "DefaultConnection": "Host=localhost;Port=5499;Username=postgres;Password=postgres;Database=Customers;"
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Commands/Handlers/RegisterUserCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using System;
3 | using System.Threading;
4 | using Services.Identity.Data;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 | using Shared.Kafka;
8 |
9 | namespace Services.Identity.Commands.Handlers
10 | {
11 | public class RegisterUserCommandHandler : AsyncRequestHandler
12 | {
13 | private readonly IdentityDBContext _dbContext;
14 | private readonly IKafkaMessageBus _bus;
15 |
16 | public RegisterUserCommandHandler(IdentityDBContext dbContext, IKafkaMessageBus bus)
17 | {
18 | _bus = bus;
19 | _dbContext = dbContext;
20 | }
21 |
22 | protected override async Task Handle(RegisterUserCommand command, CancellationToken cancellationToken)
23 | {
24 | if (await _dbContext.Users.AsNoTracking().AnyAsync(s => s.Email == command.Email))
25 | throw new ApplicationException("Email is already exist.");
26 |
27 | var user = new User
28 | {
29 | Id = command.Id,
30 | Password = command.Password,
31 | Email = command.Email,
32 | FirstName = command.FirstName,
33 | LastName = command.LastName,
34 | Address = command.Address
35 | };
36 |
37 | _dbContext.Users.Add(user);
38 |
39 | await _dbContext.SaveChangesAsync();
40 |
41 | await _bus.PublishAsync(command.Email, user);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Services.Identity/Commands/RegisterUserCommand.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using MediatR;
4 |
5 | namespace Services.Identity.Commands
6 | {
7 | public class RegisterUserCommand : IRequest
8 | {
9 | public Guid Id { get; set; }
10 | public string Email { get; set; }
11 | public string Password { get; set; }
12 | public string FirstName { get; set; }
13 | public string LastName { get; set; }
14 | public string Address { get; set; }
15 | public RegisterUserCommand()
16 | {
17 | Id = Guid.NewGuid();
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Controllers/AccountsController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using MediatR;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Services.Identity.Commands;
6 | using Services.Identity.Data;
7 |
8 | namespace Services.Identity.Controllers
9 | {
10 | [Route("api")]
11 | [ApiController]
12 | [AllowAnonymous]
13 | public class AccountsController : ControllerBase
14 | {
15 | private readonly IMediator _mediator;
16 |
17 | public AccountsController(IMediator mediator)
18 | {
19 | _mediator = mediator;
20 | }
21 |
22 | [HttpPost("sign-up")]
23 | public async Task RegisterUser([FromBody] RegisterUserCommand command)
24 | {
25 | return Ok(await _mediator.Send(command));
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Data/DbInitilializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace Services.Identity.Data
6 | {
7 | public static class DbInitilializer
8 | {
9 | public static void Initialize(IServiceProvider serviceProvider)
10 | {
11 | using (var scope = serviceProvider.CreateScope())
12 | {
13 | var context = scope.ServiceProvider.GetRequiredService();
14 | context.Database.Migrate();
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Data/Entity/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Services.Identity.Data
4 | {
5 | public class User
6 | {
7 | public Guid Id { get; set; }
8 | public string Email { get; set; }
9 | public string Password { get; set; }
10 | public string FirstName { get; set; }
11 | public string LastName { get; set; }
12 | public string Address { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Data/IdentityDBContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace Services.Identity.Data
4 | {
5 | public class IdentityDBContext : DbContext
6 | {
7 | public IdentityDBContext(DbContextOptions options) : base(options)
8 | {
9 | }
10 | public DbSet Users { get; set; }
11 |
12 | protected override void OnModelCreating(ModelBuilder modelBuilder)
13 | {
14 | modelBuilder.HasDefaultSchema("identity");
15 | modelBuilder.Entity().ToTable("users");
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Services.Identity/Data/Migrations/20200910205638_initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
8 | using Services.Identity.Data;
9 |
10 | namespace Services.Identity.Data.Migrations
11 | {
12 | [DbContext(typeof(IdentityDBContext))]
13 | [Migration("20200910205638_initial")]
14 | partial class initial
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasDefaultSchema("identity")
21 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
22 | .HasAnnotation("ProductVersion", "3.1.2")
23 | .HasAnnotation("Relational:MaxIdentifierLength", 63);
24 |
25 | modelBuilder.Entity("Services.Identity.Data.User", b =>
26 | {
27 | b.Property("Id")
28 | .ValueGeneratedOnAdd()
29 | .HasColumnType("uuid");
30 |
31 | b.Property("Address")
32 | .HasColumnType("text");
33 |
34 | b.Property("Email")
35 | .HasColumnType("text");
36 |
37 | b.Property("FirstName")
38 | .HasColumnType("text");
39 |
40 | b.Property("LastName")
41 | .HasColumnType("text");
42 |
43 | b.Property("Password")
44 | .HasColumnType("text");
45 |
46 | b.HasKey("Id");
47 |
48 | b.ToTable("users");
49 | });
50 | #pragma warning restore 612, 618
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Services.Identity/Data/Migrations/20200910205638_initial.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | namespace Services.Identity.Data.Migrations
5 | {
6 | public partial class initial : Migration
7 | {
8 | protected override void Up(MigrationBuilder migrationBuilder)
9 | {
10 | migrationBuilder.EnsureSchema(
11 | name: "identity");
12 |
13 | migrationBuilder.CreateTable(
14 | name: "users",
15 | schema: "identity",
16 | columns: table => new
17 | {
18 | Id = table.Column(nullable: false),
19 | Email = table.Column(nullable: true),
20 | Password = table.Column(nullable: true),
21 | FirstName = table.Column(nullable: true),
22 | LastName = table.Column(nullable: true),
23 | Address = table.Column(nullable: true)
24 | },
25 | constraints: table =>
26 | {
27 | table.PrimaryKey("PK_users", x => x.Id);
28 | });
29 | }
30 |
31 | protected override void Down(MigrationBuilder migrationBuilder)
32 | {
33 | migrationBuilder.DropTable(
34 | name: "users",
35 | schema: "identity");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Services.Identity/Data/Migrations/IdentityDBContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
6 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 | using Services.Identity.Data;
8 |
9 | namespace Services.Identity.Data.Migrations
10 | {
11 | [DbContext(typeof(IdentityDBContext))]
12 | partial class IdentityDBContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasDefaultSchema("identity")
19 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
20 | .HasAnnotation("ProductVersion", "3.1.2")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 63);
22 |
23 | modelBuilder.Entity("Services.Identity.Data.User", b =>
24 | {
25 | b.Property("Id")
26 | .ValueGeneratedOnAdd()
27 | .HasColumnType("uuid");
28 |
29 | b.Property("Address")
30 | .HasColumnType("text");
31 |
32 | b.Property("Email")
33 | .HasColumnType("text");
34 |
35 | b.Property("FirstName")
36 | .HasColumnType("text");
37 |
38 | b.Property("LastName")
39 | .HasColumnType("text");
40 |
41 | b.Property("Password")
42 | .HasColumnType("text");
43 |
44 | b.HasKey("Id");
45 |
46 | b.ToTable("users");
47 | });
48 | #pragma warning restore 612, 618
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Services.Identity/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Services.Identity
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Services.Identity/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:55301",
8 | "sslPort": 44356
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "Api": {
20 | "commandName": "Project",
21 | "launchBrowser": true,
22 | "applicationUrl": "http://localhost:5001",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Services.Identity/README.md:
--------------------------------------------------------------------------------
1 | **Migration**
2 |
3 | 0- cd Services.Identity
4 |
5 | 1- *dotnet ef migrations add "migration_name" -o ./Data/Migrations*
6 |
7 | 2- *dotnet ef database update*
--------------------------------------------------------------------------------
/src/Services.Identity/Services.Identity.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | InProcess
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 | all
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Services.Identity/Startup.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using MediatR;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.Hosting;
9 | using Services.Identity.Commands.Handlers;
10 | using Services.Identity.Data;
11 | using Shared.Kafka;
12 |
13 | namespace Services.Identity
14 | {
15 | public class Startup
16 | {
17 | public Startup(IConfiguration configuration)
18 | {
19 | Configuration = configuration;
20 | }
21 |
22 | public IConfiguration Configuration { get; }
23 |
24 | public void ConfigureServices(IServiceCollection services)
25 | {
26 | services.AddDbContext(options =>
27 | options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
28 | );
29 |
30 | services.AddMediatR(typeof(RegisterUserCommandHandler).GetTypeInfo().Assembly);
31 |
32 | services.AddControllers();
33 |
34 | services.AddKafkaMessageBus();
35 |
36 | services.AddKafkaProducer(p =>
37 | {
38 | p.Topic = "users";
39 | p.BootstrapServers = "localhost:9092";
40 | });
41 | }
42 |
43 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
44 | {
45 | if (env.IsDevelopment())
46 | {
47 | app.UseDeveloperExceptionPage();
48 | }
49 |
50 | DbInitilializer.Initialize(app.ApplicationServices);
51 |
52 | app.UseRouting();
53 | app.UseAuthorization();
54 | app.UseEndpoints(endpoints =>
55 | {
56 | endpoints.MapControllers();
57 | });
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Services.Identity/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Services.Identity/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "DefaultConnection": "Host=localhost;Port=5499;Username=postgres;Password=postgres;Database=Identity;"
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/Consumer/BackGroundKafkaConsumer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Confluent.Kafka;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using Microsoft.Extensions.Options;
8 |
9 | namespace Shared.Kafka.Consumer
10 | {
11 | public class BackGroundKafkaConsumer : BackgroundService
12 | {
13 | private readonly KafkaConsumerConfig _config;
14 | private IKafkaHandler _handler;
15 | private readonly IServiceScopeFactory _serviceScopeFactory;
16 |
17 | public BackGroundKafkaConsumer(IOptions> config,
18 | IServiceScopeFactory serviceScopeFactory)
19 | {
20 | _serviceScopeFactory = serviceScopeFactory;
21 | _config = config.Value;
22 | }
23 |
24 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
25 | {
26 | using (var scope = _serviceScopeFactory.CreateScope())
27 | {
28 | _handler = scope.ServiceProvider.GetRequiredService>();
29 |
30 | var builder = new ConsumerBuilder(_config).SetValueDeserializer(new KafkaDeserializer());
31 |
32 | using (IConsumer consumer = builder.Build())
33 | {
34 | consumer.Subscribe(_config.Topic);
35 |
36 | while (!stoppingToken.IsCancellationRequested)
37 | {
38 | var result = consumer.Consume(TimeSpan.FromMilliseconds(1000));
39 |
40 | if (result != null)
41 | {
42 | await _handler.HandleAsync(result.Message.Key, result.Message.Value);
43 |
44 | consumer.Commit(result);
45 |
46 | consumer.StoreOffset(result);
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/Consumer/IKafkaHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Shared.Kafka.Consumer
4 | {
5 | public interface IKafkaHandler
6 | {
7 | Task HandleAsync(Tk key, Tv value);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/Consumer/KafkaConsumerConfig.cs:
--------------------------------------------------------------------------------
1 | using Confluent.Kafka;
2 |
3 | namespace Shared.Kafka.Consumer
4 | {
5 | public class KafkaConsumerConfig : ConsumerConfig
6 | {
7 | public string Topic { get; set; }
8 | public KafkaConsumerConfig()
9 | {
10 | AutoOffsetReset = Confluent.Kafka.AutoOffsetReset.Earliest;
11 | EnableAutoOffsetStore = false;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/IKafkaMessageBus.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Shared.Kafka
4 | {
5 | public interface IKafkaMessageBus
6 | {
7 | Task PublishAsync(Tk key, Tv message);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/KafkaDeserializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Confluent.Kafka;
4 | using Newtonsoft.Json;
5 |
6 | namespace Shared.Kafka
7 | {
8 | internal sealed class KafkaDeserializer : IDeserializer
9 | {
10 | public T Deserialize(ReadOnlySpan data, bool isNull, SerializationContext context)
11 | {
12 | if (typeof(T) == typeof(Null))
13 | {
14 | if (data.Length > 0)
15 | throw new ArgumentException("The data is null not null.");
16 | return default;
17 | }
18 |
19 | if (typeof(T) == typeof(Ignore))
20 | return default;
21 |
22 | var dataJson = Encoding.UTF8.GetString(data);
23 |
24 | return JsonConvert.DeserializeObject(dataJson);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/KafkaMessageBus.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Shared.Kafka.Producer;
3 |
4 | namespace Shared.Kafka
5 | {
6 | public class KafkaMessageBus : IKafkaMessageBus
7 | {
8 | public readonly KafkaProducer _producer;
9 | public KafkaMessageBus(KafkaProducer producer)
10 | {
11 | _producer = producer;
12 | }
13 | public async Task PublishAsync(Tk key, Tv message)
14 | {
15 | await _producer.ProduceAsync(key, message);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/KafkaSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Confluent.Kafka;
4 | using Newtonsoft.Json;
5 |
6 | namespace Shared.Kafka
7 | {
8 | internal sealed class KafkaSerializer : ISerializer
9 | {
10 | public byte[] Serialize(T data, SerializationContext context)
11 | {
12 | if (typeof(T) == typeof(Null))
13 | return null;
14 |
15 | if (typeof(T) == typeof(Ignore))
16 | throw new NotSupportedException("Not Supported.");
17 |
18 | var json = JsonConvert.SerializeObject(data);
19 |
20 | return Encoding.UTF8.GetBytes(json);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/Producer/KafkaProducer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Confluent.Kafka;
4 | using Microsoft.Extensions.Options;
5 |
6 | namespace Shared.Kafka.Producer
7 | {
8 | public class KafkaProducer : IDisposable
9 | {
10 | private readonly IProducer _producer;
11 | private readonly string _topic;
12 |
13 | public KafkaProducer(IOptions> topicOptions, IProducer producer)
14 | {
15 | _topic = topicOptions.Value.Topic;
16 | _producer = producer;
17 | }
18 |
19 | public async Task ProduceAsync(Tk key, Tv value)
20 | {
21 | await _producer.ProduceAsync(_topic, new Message { Key = key, Value = value });
22 | }
23 |
24 | public void Dispose()
25 | {
26 | _producer.Dispose();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/Producer/KafkaProducerConfig.cs:
--------------------------------------------------------------------------------
1 | using Confluent.Kafka;
2 |
3 | namespace Shared.Kafka.Producer
4 | {
5 | public class KafkaProducerConfig : ProducerConfig
6 | {
7 | public string Topic { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Shared/Kafka/RegisterServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Shared.Kafka.Consumer;
3 | using Shared.Kafka.Producer;
4 |
5 | using Confluent.Kafka;
6 |
7 | using Microsoft.Extensions.Options;
8 | using Microsoft.Extensions.DependencyInjection;
9 |
10 | namespace Shared.Kafka
11 | {
12 | public static class RegisterServiceExtensions
13 | {
14 | public static IServiceCollection AddKafkaMessageBus(this IServiceCollection serviceCollection)
15 | => serviceCollection.AddSingleton(typeof(IKafkaMessageBus<,>), typeof(KafkaMessageBus<,>));
16 |
17 | public static IServiceCollection AddKafkaConsumer(this IServiceCollection services,
18 | Action> configAction) where THandler : class, IKafkaHandler
19 | {
20 | services.AddScoped, THandler>();
21 |
22 | services.AddHostedService>();
23 |
24 | services.Configure(configAction);
25 |
26 | return services;
27 | }
28 |
29 | public static IServiceCollection AddKafkaProducer(this IServiceCollection services,
30 | Action> configAction)
31 | {
32 | services.AddConfluentKafkaProducer();
33 |
34 | services.AddSingleton>();
35 |
36 | services.Configure(configAction);
37 |
38 | return services;
39 | }
40 |
41 | private static IServiceCollection AddConfluentKafkaProducer(this IServiceCollection services)
42 | {
43 | services.AddSingleton(
44 | sp =>
45 | {
46 | var config = sp.GetRequiredService>>();
47 |
48 | var builder = new ProducerBuilder(config.Value).SetValueSerializer(new KafkaSerializer());
49 |
50 | return builder.Build();
51 | });
52 |
53 | return services;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/Shared/Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------