Skip to content

Commit 2c00798

Browse files
author
Anthony Sneed
committed
Rename solution, update ReadMe.md.
1 parent 64a571c commit 2c00798

File tree

4 files changed

+91
-43
lines changed

4 files changed

+91
-43
lines changed

EventDriven.CQRS.sln renamed to EventDriven.ReferenceArchitecture.sln

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ EndProject
1111
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{269CD137-4093-4100-B33E-808586D335F6}"
1212
EndProject
1313
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "reference-architecture", "reference-architecture", "{C4FD0AF1-927A-4860-A634-7CE342807692}"
14+
ProjectSection(SolutionItems) = preProject
15+
reference-architecture\tye.yaml = reference-architecture\tye.yaml
16+
EndProjectSection
1417
EndProject
1518
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventDriven.CQRS.Tests", "test\EventDriven.CQRS.Tests\EventDriven.CQRS.Tests.csproj", "{9809006C-2F6B-44A1-8AE2-BC449368D209}"
1619
EndProject

ReadMe.md

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# EventDriven.CQRS
1+
# EventDriven.ReferenceArchitecture
22

3-
An event-driven approach to Command Query Responsibility Segregation.
3+
Reference architecture for using **EventDriven** abstractions and libraries for Domain Driven Design (**DDD**), Command-Query Responsibility Segregation (**CQRS**) and Event Driven Architecture (**EDA**).
44

55
## Prerequisites
66
- [.NET Core SDK](https://dotnet.microsoft.com/download) (5.0 or greater)
@@ -12,10 +12,11 @@ An event-driven approach to Command Query Responsibility Segregation.
1212
- [Dapr](https://dapr.io/) (Distributed Application Runtime)
1313
- [Install Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
1414
- [Initialize Dapr](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
15+
- [Microsoft Tye](https://github.com/dotnet/tye/blob/main/docs/getting_started.md) (recommended)
1516

1617
## Introduction
1718

18-
This project builds on the principles of [Domain Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design) to provide a set of abstractions and reference architecture for implementing the [Command Query Responsibility Segregation](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs) pattern, also known as as CQRS. Because entities process commands by emitting domain events, adding [event sourcing](https://microservices.io/patterns/data/event-sourcing.html) at a later time will be relatively straightforward.
19+
This project builds on the principles of [Domain Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design) (DDD) to provide a set of abstractions and reference architecture for implementing the [Command Query Responsibility Segregation](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs) (CQRS) pattern. By providing an event bus abstraction over [Dapr](https://dapr.io/) (Distributed Application Runtime), the reference architecture demonstrates how to apply principles of [Event Driven Architecture](https://en.wikipedia.org/wiki/Event-driven_architecture) (EDA). Because entities process commands by emitting domain events, adding [event sourcing](https://microservices.io/patterns/data/event-sourcing.html) at a later time will be relatively straightforward.
1920

2021
The **Reference Architecture** projects demonstrate how to apply these concepts to two microservices: `CustomerService` and `OrderService`. In addition, each service has *separate controllers for read and write operations*, thus segregating command and query responsibilities, with different sets of models, or Data Transfer Objects (DTO's).
2122
- **Query Controller**: Uses repository to retrieve entities and converts them to DTO's with AutoMapper.
@@ -30,21 +31,22 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
3031
<img width="600" src="images/event-driven-cqrs-ref-arch.png">
3132
</p>
3233

33-
### Reference Architecture
34+
### Run services with Tye and Dapr
3435

35-
1. Run Dapr Dashboard.
36-
- Then open http://localhost:8080 to view containers after executing `dapr run` commands.
37-
```
38-
dapr dashboard
39-
```
40-
2. Use Tye or Dapr to run the customer service.
36+
> **Note**: As an alternative to Tye, you can run services directly usng the Dapr CLI. This may be useful for troubleshooting Dapr issues after setting `Microsoft.AspNetCore` logging level to `Debug`.
37+
> `dapr run --app-id service-name --app-port #### --components-path ../dapr/components -- dotnet run`
38+
39+
1. Open a terminal at the **reference-architecture** directory and run Tye to launch all services simultaneously.
4140
```
42-
dapr run --app-id customer-service --app-port 5656 --components-path ../dapr/components -- dotnet run
41+
tye run
4342
```
44-
3. Use Dapr to run the order service.
43+
2. Alternatively, run Tye in debug mode.
4544
```
46-
dapr run --app-id order-service --app-port 5757 --components-path ../dapr/components -- dotnet run
45+
tye run --debug *
4746
```
47+
- Set breakpoints in **OrderService**, **CustomerService**.
48+
- Attach the IDE debugger to **OrderService.dll**, **CustomerService.dll**.
49+
3. Open the Tye dashboard at http://localhost:8000 to inspect service endpoints and view logs.
4850
4. Create some customers.
4951
- Open http://localhost:5656/swagger
5052
- Execute posts using contents of **customers.json**.
@@ -64,12 +66,6 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
6466
6. Update the address of a customer who has order.
6567
- Note the address is also updated for the customer's orders.
6668
- Observe log messages in terminal when integration events are published and handled.
67-
7. To **debug** services, you can use [tye](https://github.com/dotnet/tye) with the *Dapr extension*.
68-
- Open a terminal at the reference-architecture folder.
69-
- Run `tye run --debug *`
70-
- Set breakpoints in each project.
71-
- Attach the debugger to each process.
72-
- Open http://localhost:8000 to view the Tye Dashboard.
7369
7470
### Development Guide
7571
@@ -87,16 +83,23 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
8783
```
8884
- Where you need to execute business logic, implement `ICommandProcessor` and `IEventApplier` interfaces to process commands by emitting domain events and to apply those events to mutate entity state.
8985
```csharp
90-
public IEnumerable<IDomainEvent> Process(CreateCustomer command)
91-
// To process command, return one or more domain events
92-
=> new List<IDomainEvent>
93-
{
94-
new CustomerCreated(command.Customer)
95-
};
86+
public class Customer :
87+
Entity,
88+
ICommandProcessor<CreateCustomer, CustomerCreated>,
89+
IEventApplier<CustomerCreated>
90+
{
91+
public string FirstName { get; set; }
92+
public string LastName { get; set; }
93+
public Address ShippingAddress { get; set; }
94+
95+
public CustomerCreated Process(CreateCustomer command)
96+
// To process command, return one or more domain events
97+
=> new(command.Entity);
9698
97-
public void Apply(CustomerCreated domainEvent) =>
98-
// Set Id
99-
Id = domainEvent.EntityId != default(Guid) ? domainEvent.EntityId : Guid.NewGuid();
99+
public void Apply(CustomerCreated domainEvent) =>
100+
// Set Id
101+
Id = domainEvent.EntityId != default ? domainEvent.EntityId : Guid.NewGuid();
102+
}
100103
```
101104
2. Add a `CustomerCommandHandler` class that implements `ICommandHandler` for create, update and remove commands.
102105
- Inject `ICustomerRepository`, `IEventBus` and `IMapper` into the ctor.
@@ -105,16 +108,13 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
105108
public async Task<CommandResult<Customer>> Handle(CreateCustomer command)
106109
{
107110
// Process command
108-
_logger.LogInformation("Handling command: {commandName}", nameof(CreateCustomer));
109-
var events = command.Customer.Process(command);
111+
var domainEvent = command.Entity.Process(command);
110112
111113
// Apply events
112-
var domainEvent = events.OfType<CustomerCreated>().SingleOrDefault();
113-
if (domainEvent == null) return new CommandResult<Customer>(CommandOutcome.NotHandled);
114-
command.Customer.Apply(domainEvent);
114+
command.Entity.Apply(domainEvent);
115115
116116
// Persist entity
117-
var entity = await _repository.Add(command.Customer);
117+
var entity = await _repository.AddAsync(command.Entity);
118118
if (entity == null) return new CommandResult<Customer>(CommandOutcome.InvalidCommand);
119119
return new CommandResult<Customer>(CommandOutcome.Accepted, entity);
120120
}
@@ -130,21 +130,20 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
130130
public async Task<CommandResult<Customer>> Handle(UpdateCustomer command)
131131
{
132132
// Compare shipping addresses
133-
_logger.LogInformation("Handling command: {commandName}", nameof(UpdateCustomer));
134-
var existing = await _repository.Get(command.EntityId);
135-
var addressChanged = command.Customer.ShippingAddress != existing.ShippingAddress;
133+
var existing = await _repository.GetAsync(command.EntityId);
134+
if (existing == null) return new CommandResult<Customer>(CommandOutcome.NotHandled);
135+
var addressChanged = command.Entity.ShippingAddress != existing.ShippingAddress;
136136
137137
try
138138
{
139139
// Persist entity
140-
var entity = await _repository.Update(command.Customer);
140+
var entity = await _repository.UpdateAsync(command.Entity);
141141
if (entity == null) return new CommandResult<Customer>(CommandOutcome.NotFound);
142142
143143
// Publish events
144144
if (addressChanged)
145145
{
146146
var shippingAddress = _mapper.Map<Integration.Models.Address>(entity.ShippingAddress);
147-
_logger.LogInformation("Publishing event: {eventName}", $"v1.{nameof(CustomerAddressUpdated)}");
148147
await _eventBus.PublishAsync(
149148
new CustomerAddressUpdated(entity.Id, shippingAddress),
150149
null, "v1");
@@ -161,14 +160,59 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
161160
- Add Post, Put and Delete actions which accept a `Customer` DTO, map it to a `Customer` entity and invoke the appropriate command handler.
162161
4. Add a `CustomerQueryController` to the project that injects a `ICustomerRepository` into the ctor.
163162
- Use the repository to retrieve entities, then map those to `Customer` DTO objects.
164-
5. Repeat these steps for the Order service.
163+
5. Register dependencies in `Startup.ConfigureServices`.
164+
```csharp
165+
// Registrations
166+
services.AddAutoMapper(typeof(Startup));
167+
services.AddSingleton<CustomerCommandHandler>();
168+
services.AddSingleton<ICustomerRepository, CustomerRepository>();
169+
services.AddMongoDbSettings<CustomerDatabaseSettings, Customer>(Configuration);
170+
171+
// Add Dapr event bus
172+
services.AddDaprEventBus(Configuration, true);
173+
174+
// Add Dapr Mongo event cache
175+
services.AddDaprMongoEventCache(Configuration);
176+
```
177+
6. Add configuration entries to **appsettings.json**.
178+
```json
179+
"CustomerDatabaseSettings": {
180+
"ConnectionString": "mongodb://localhost:27017",
181+
"DatabaseName": "CustomersDb",
182+
"CollectionName": "Customers"
183+
},
184+
"DaprEventBusOptions": {
185+
"PubSubName": "pubsub"
186+
},
187+
"DaprEventCacheOptions": {
188+
"DaprStateStoreOptions": {
189+
"StateStoreName": "statestore-mongodb"
190+
}
191+
},
192+
"DaprStoreDatabaseSettings": {
193+
"ConnectionString": "mongodb://localhost:27017",
194+
"DatabaseName": "daprStore",
195+
"CollectionName": "daprCollection"
196+
},
197+
"DaprEventBusSchemaOptions": {
198+
"UseSchemaRegistry": true,
199+
"SchemaValidatorType": "Json",
200+
"SchemaRegistryType": "Mongo",
201+
"AddSchemaOnPublish": true,
202+
"MongoStateStoreOptions": {
203+
"ConnectionString": "mongodb://localhost:27017",
204+
"DatabaseName": "schema-registry",
205+
"SchemasCollectionName": "schemas"
206+
}
207+
}
208+
```
209+
7. Repeat these steps for the **Order** service.
165210
- Reference the Common project.
166211
- Add **Integration/EventHandlers** folders with a `CustomerAddressUpdatedEventHandler` class that extends `IntegrationEventHandler<CustomerAddressUpdated>`.
167212
- Override `HandleAsync` to update the order addresses for the customer.
168213
```csharp
169214
public override async Task HandleAsync(CustomerAddressUpdated @event)
170215
{
171-
_logger.LogInformation("Handling CustomerAddressUpdated event.");
172216
var orders = await _orderRepository.GetCustomerOrders(@event.CustomerId);
173217
foreach (var order in orders)
174218
{
@@ -180,7 +224,8 @@ The **Reference Architecture** projects demonstrate how to apply these concepts
180224
- In `Startup.ConfigureServices` register `CustomerAddressUpdatedEventHandler` then add the Dapr Event Bus.
181225
```csharp
182226
services.AddSingleton<CustomerAddressUpdatedEventHandler>();
183-
services.AddDaprEventBus(Constants.DaprPubSubName);
227+
services.AddDaprEventBus(Configuration, true);
228+
services.AddDaprMongoEventCache(Configuration);
184229
```
185230
- In `Startup.Configure` use Cloud Events, map subscribe handlers, and map Dapr Event Bus endpoints, subscribing with the event handler.
186231
```csharp
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<mxfile host="app.diagrams.net" modified="2021-04-24T21:13:00.498Z" agent="5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" etag="jcSRvpNV_wRMNOIdpFs0" version="14.6.1" type="device"><diagram id="H1tdGHeVPZ8NpCb7zYOw" name="Page-1">7Vzbdto4FP0a1uo8pMtXLo8E0nbWZKZpaKfTvglbMZoKyyPLCfTrR7LlqwSBgMG0fcI+snzZ++yjoyObnj1Zrt5SEC3+JD7EPcvwVz172rMs0+zb/EdY1pmlb44yQ0CRLw8qDTP0HUqjIa0J8mFcO5ARghmK6kaPhCH0WM0GKCVP9cMeCK5fNQIBVAwzD2DV+hn5bJFZh65R2t9BFCzyK5uGbFmC/GBpiBfAJ08Vk33TsyeUEJZtLVcTiAV4OS5ZvzcbWosbozBku3T4+nC/mHvE8Vd/3H1affz7HfzqXtmSjZit8yeGPgdA7oYk5D/XlCShD8V5DL5HKFuQgIQA3xIScaPJjf9CxtaSPpAwwk0LtsSyFa4Q+0d0f225cvdLpWm6kqdOd9b5TsjoOutlZt2E4Uu1teyY7tV63kGKlpBBKo0PJGTyBk2H72cPLp52I6DSFJOEenALiqYrPRPQALJtcLsF8VwxkPD7o2vekUIMGHqs3wmQrhsUx5Xs8g1J8B5k57f5CHAiLzUhyyUIfW58x38wB6vpDnXunxaIwVkEUjieuOTrPDcxfkAYTwgmtPSlR0gZ4voaYxSE3MyEC0kyRBtcbadDBU92GMhIIwON3Ze6eypla+a2RVWyA6MtvBW4bx7Fw1jGlHKyQ74x+XA/4z/38AHy5+SoWsaYegJkjyUUKmxweFgd8phR8g02QNbgDiTeHr8DoQmFiCXyfXEZLcd1L6jSPMz35U2aR6DSsgc1Lh2VSsfUUOm0xaQaJQ8PiWV02yu2VbHv7xXGno1OwwODk+x6R1Dq5Kucqbowi6E9P0UWXmWvBlHFbbycO0tRoUImH5ojsZks8dhjpKqOWzCH+I7EiCEiVDInjJHlxjhWIZgkDKOQKzNPTHTKOYJYHKuOr6lRi60RS7+1sHcZOcV+KcXLs4dnZcdl3IbuTLfuF659Wt1tSza4KPiwhU+Rb7SQWVg7ZhatDUfFlOJUCnuRwAYnEtiz6Xn/VEI8iNS+opcPCUyvf1lqseyuyWWgRqIk5qO4wNOYQfqIvEvItEW2tyAUfec2gKsHHDX1ridrQ5U8V8Od3Rp5Q0361seCmoRvBGLj/mb2MTfyaxT2nj3mPd9yzVv82gbP4vKtRG5MIYZM5b6Dqho8z0shtJOIKs/iNcTEEQhrgPb/S0SV69rLYBGs0GD+ip/ByHiobP0mNgVkhgD26gEsEV5nffiJwDKrFti2gHoB8SMU0lJa6ieJU3rEKUwnWjXasrsUjSGhS4DrzU8SS9HuZPeZNnK/4eK+4o/qoTDQ9hcR5ApxRwplf6Ny7bSRURDGD7xX3j+ExQFPhPr101e7z4H3LUjd9KoBquUMCzAtZ1RuuxVofRRHGEhYUSjmKeVjYwJY9YZy9nJZ3aQp3KdYhM+M8DnN2z7DuYipd6L3BKO02lEIM/MLqc0N8XZPse0ea4+gQdsc1kRY1HorIiwEV4uOrYlQLTGVQ1uTm+nH92raiDGK4k1j0T7AHwNfoxHkDFfBd6iB120NXntjjJvnqDbx5heal07e5GAcBBQGQIw5xk3IEFurx6iWO0o8GMdpKphOoWLNmaMICyynZAlQqLanpce4RQc4Za13WHcURyNEUydEs1l7Op6rDFVouzcl6/Yqys7ztIHeOU60iqJO1CrTiXsYiWIhkRdqM6tsIwQ30kxXs4iiTTPbKyZunbxNAQNzEKsZfF7R9dZYZGDUfh7xeUbP7bwwFDnW+6yUm88DJS3u+Wgy3Xom0nc1AVA3TytSluPXpDSpyI+zuOh0b3VR9N8K54+xKGUdGuw3rEr1z7wqZZ84X7jwRRJ7gz4PdAP3zIsklrMlal5W3VcJkWev+1ruiTV22csk1q7598FaPIzVH2WhpN+5hRJLTbZ/ymJ7M5Sdv9puqcsg76l/kVU+t2tVPmu00euLSl4N7F8lvt4JZlzdq/HluWG384lO1/jsnfN965w5hq0WM2QEuPQCX3NoO3+Fz7Y2Yv2zlvfc7pX3bHUpLGfp1xs0W9/HPf8rNM6pa03VcasYqvYat4piZFfGLecixi3HuYQMZTAYVbm+Ml4b7jH4hqE/Fp/8lU/KLW8QzpUec/ZYfoSHQRwjLzfLw8xzuc1ZXz21NVXIcpFt7PtUTk0WIAy44+hnHr3TrfOcIjMa7Drpby0zckwFyA6qWeDaULM56Fr43vkDzfPqUPPNxF46/J3LIaAg/VbJMqQqL1CLnZOirRadlRrRFES0RN24TmJ9wWgjH+al8DEcqXwMTsuHWqm+hz7SFM1e3RI+cfhNgT2fQjKOMfxOxNWuo0pEKuyVMPUsO2gF8/8wOCNbVmNtezTQTB91nwS29q6CrZavx59nKlmzv7jxevZB05TxyM9BhMgmmCS+SmpZ6iwqBOLAzuqqWeQc6Sb6umJ4e0VORy3HFFmdkdOCclaiZI5RvIDVUIfKUGeg2oAEtQPSjnWCtooCznGoNBufOY9MlUo+aL12VTKt1rhU84miWFNwWX8rv8FvnMxjj6L5JoI3vNtv8BzzYsivFoCMI3hCc4FL6wkDjajb8wM1eyHqJK2b9bpjRFnLqhMyUlccdStJL+CD75b/6JO9JFT+L5J98z8=</diagram></mxfile>
1+
<mxfile host="Chrome" modified="2022-02-26T19:54:06.177Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" etag="pkddOFWBH00zwF35pywn" version="16.2.2" type="device"><diagram id="H1tdGHeVPZ8NpCb7zYOw" name="Page-1">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</diagram></mxfile>

images/event-driven-cqrs-ref-arch.png

-538 Bytes
Loading

0 commit comments

Comments
 (0)