Skip to content

Reference architecture for using EventDriven abstractions and libraries for Domain Driven Design (DDD), Command-Query Responsibility Segregation (CQRS) and Event Driven Architecture (EDA).

License

event-driven-dotnet/EventDriven.ReferenceArchitecture

Repository files navigation

EventDriven.ReferenceArchitecture

Reference architecture for using Event Driven .NET abstractions and libraries for Domain Driven Design (DDD), Command Query Responsibility Segregation (CQRS) and Event Driven Architecture (EDA).

Prerequisites

Introduction

This project builds on the principles of Domain Driven Design (DDD) to provide a set of abstractions and reference architecture for implementing the Command Query Responsibility Segregation (CQRS) pattern. By providing an event bus abstraction over Dapr (Distributed Application Runtime), the reference architecture demonstrates how to apply principles of Event Driven Architecture (EDA). Because entities process commands by emitting domain events, adding event sourcing at a later time will be relatively straightforward.

Note: EventDriven.CQRS.Abstractions version 2.0 or later uses MediatR to enable a handler per command pattern with behaviors for cross-cutting concerns.

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).

  • Command Controller: Converts DTO's to domain entities using AutoMapper. Passes commands to a command broker, which selects the appropriate command handler for executing business logic.
  • Command Handlers: Uses a domain entity to process commands which generate one or more domain events, then requests entity to apply the domain events in order to mutate entity state. Persists entity state using a repository abstraction and optionally publishes an integration event which is handled by another microservice.
  • Query Controller: Passes queries to a query broker, which selects the appropriate query handler to retrieve entities. Converts entities to DTO's with AutoMapper.
  • Query Handlers: Processes queries to retrieve entities using a repository abstraction.
  • Behaviors: Used to implement cross-cutting concerns such as logging or validation.
  • Repository: Used to persist or retrieve entities from a database.
  • Event Bus: Used to publish integration events, as well as subscribe to events using an event handler. Dapr is used to abstract away the underlying pub/sub implementation. The default is Redis (for local development), but Dapr can be configured to use other components, such as AWS SNS+SQS.

Note: This example illustrates a simple CQRS implementation with a shared database and single service for both read and write operations. A more sophisticated implementation might entail separate services and databases for read and write operations, using integration events to communicate between them. This simple example only uses integration events to communicate between the customer and order services.

Running Services with Aspire

  1. If using an IDE such as Visual Studio or Rider, Using an IDE such as Visual Studio or Rider, run the http profile of ReferenceArchitecture.AppHost.
  2. Open the Aspire dashboard to inspect service endpoints and view logs.
  3. Create some customers.
    • Open http://localhost:5656/swagger
    • Execute posts using contents of customers.json.
    • Copy post response, modify fields, then execute puts.
      • Make sure to copy etag value from last response, or you will get a concurrency error.
    • Copy id and etag values to execute deletes.
    • Execute gets to retrieve customers.
    • View customers database collections using Robo 3T.
  4. Create some orders.
    • Open http://localhost:5757/swagger
    • Execute posts using contents of orders.json.
    • Copy post response, modify fields, then execute puts.
      • Make sure to copy etag value from last response, or you will get a concurrency error.
    • Copy id and etag values to execute deletes.
    • Execute gets to retrieve orders.
    • View orders database collections using Robo 3T.
  5. Update the address of a customer who has order.
    • Note the address is also updated for the customer's orders.
    • Observe log messages in terminal when integration events are published and handled.

Running Services with the Dapr CLI

  1. Start the Customer Service using the Dapr CLI from a terminal at the project root.

    dapr run --app-id customer-service --app-port 5656 --resources-path ../dapr/components -- dotnet run
    
  2. Start the Order Service using the Dapr CLI from a terminal at the project root.

    dapr run --app-id order-service --app-port 5757 --resources-path ../dapr/components -- dotnet run
    
  3. Open the Dapr Dashboard at http://localhost:8080

    dapr dashboard
    
  4. Execute steps 3-5 above to use services and pub-sub features.

Running Tests

Unit Tests

In the test folder you'll find unit tests for both CustomerService and OrderService projects.

  • xUnit is used as the unit testing framework.
  • Moq is used as the mocking framework.

Note: Because database API's are notoriously difficult to mock, repositories are deliberately excluded from unit testing. Instead, repositories attain code coverage with acceptance/integration tests.

  1. Run unit CustomerService.Tests and OrderService.Tests from the Test explorer in your IDE.
  2. Alternatively, open a terminal at CustomerService.Tests and OrderService.Tests, then run dotnet test

Acceptance (Integration) Tests

In the tests folder you'll find an EventDriven.ReferenceArchitecture.Specs project with automated acceptance / integration tests.

  1. Using an IDE such as Visual Studio or Rider, run the specs profile of ReferenceArchitecture.AppHost.
  2. Run EventDriven.ReferenceArchitecture.Specs from the Test explorer of your IDE.
  3. Alternatively, open a terminal at EventDriven.ReferenceArchitecture.Specs, then run dotnet test

Development Guide

For step-by-step instructions on how to build microservices with Event Driven .NET using this reference architecture, please see the Event Driven .NET Development Guide.

About

Reference architecture for using EventDriven abstractions and libraries for Domain Driven Design (DDD), Command-Query Responsibility Segregation (CQRS) and Event Driven Architecture (EDA).

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published