Domain Driven Design Application Services
MBark December 03, 2023
Introduction
Domain-Driven Design (DDD) is a set of patterns and ideas intended to help developers create complex software systems that are highly aligned with the business domain. Application Services is a key DDD concept that is explored in this article along with several C# examples for clarity.
What are Application Services?
An integral component of the DDD architectural style is Application Services. They operate as the entry point for overseeing duties unique to a given application and coordinating communication between various domain objects and infrastructure tiers. These services give application customers a high-level interface while encapsulating business logic.
The primary role of an Application Service is to coordinate the execution of numerous domain objects and entities, assuring the fulfilment of a certain use case or business transaction. It serves as a barrier between the display and domain layers. Application Services are frequently used to actualize use cases defined in the domain by coordinating the operations of many domain objects to achieve the desired result.
Application Services Examples
Let's look at an example with an e-commerce application in charge of managing orders and inventory. Our focus will be on two critical Application Services: OrderApplicationService and InventoryApplicationService.
public class OrderApplicationService { private readonly IOrderRepository _orderRepository; private readonly IInventoryRepository _inventoryRepository; public OrderApplicationService(IOrderRepository orderRepository, IInventoryRepository inventoryRepository) { _orderRepository = orderRepository; _inventoryRepository = inventoryRepository; } public void PlaceOrder(Guid productId, int quantity) { var product = _inventoryRepository.GetProduct(productId); var availableQuantity = _inventoryRepository.GetAvailableQuantity(productId); if (availableQuantity >= quantity) { var order = new Order(Guid.NewGuid(), product, quantity); _orderRepository.Save(order); _inventoryRepository.DecreaseQuantity(productId, quantity); // Other operations like sending notifications, updating metrics, etc. } else { throw new InvalidOperationException("Insufficient quantity available."); } } }
The PlaceOrder
method oversees the ordering process within the OrderApplicationService
. It retrieves product information as well as available quantities from the IInventoryRepository
. If there is an adequate quantity, it creates a new Order object, puts it in the IOrderRepository
, and simultaneously reduces the inventory quantity by the ordered amount.
public class InventoryApplicationService { private readonly IInventoryRepository _inventoryRepository; public InventoryApplicationService(IInventoryRepository inventoryRepository) { _inventoryRepository = inventoryRepository; } public void AddProduct(Guid productId, string name, decimal price) { var product = new Product(productId, name, price); _inventoryRepository.SaveProduct(product); } public void UpdateProduct(Guid productId, string name, decimal price) { var product = _inventoryRepository.GetProduct(productId); if (product != null) { product.UpdateDetails(name, price); _inventoryRepository.SaveProduct(product); } } }
The InventoryApplicationService
manages inventory, including features for introducing new products and changing the details of current ones. It communicates with the IInventoryRepository to ensure the long-term viability of these changes.
Is it possible to use CQRS instead of Application Services?
CQRS (Command Query Responsibility Segregation) is an architectural paradigm that is becoming increasingly prominent in the software development field. It encourages the separation of commands (write operations) and queries (read operations) into separate models in order to improve performance, scalability, and maintainability. Given the joint roles of Application Services and CQRS, it's natural to wonder whether CQRS can completely replace Application Services. Let us explore deeper into this issue.
What is CQRS?
The command side manages write operations and maintains the system's state in a CQRS-based design, while the query side handles data retrieval in a denormalized and optimized format. Communication between these two parties takes place via explicit messages, which are typically sent via message queues or event-driven structures.
While CQRS is effective for complex systems with high write and read demands, it is critical to understand that CQRS focuses on individual operations or queries. Application Services, on the other hand, are designed to encapsulate and orchestrate whole use cases or business functions.
CQRS's Complementary Characteristics
Although CQRS and Application Services have similarities in terms of managing commands and encapsulating business logic, their aims are distinct, and they can effectively complement each other.
CQRS can be used within Application Services to manage individual commands or inquiries, making it easier to update the system's state. Application Services, on the other hand, provide a higher level of abstraction, coordinating the execution of many commands or queries to meet a given use case.
CQRS in Application Services: An Example
Let's go back to our e-commerce example and see how CQRS might be used in conjunction with Application Services.
public class OrderApplicationService { private readonly ICommandBus _commandBus; private readonly IQueryBus _queryBus; public OrderApplicationService(ICommandBus commandBus, IQueryBus queryBus) { _commandBus = commandBus; _queryBus = queryBus; } public void PlaceOrder(Guid productId, int quantity) { var product = _queryBus.Send(new GetProductQuery(productId)); var availableQuantity = _queryBus.Send(new GetAvailableQuantityQuery(productId)); if (availableQuantity >= quantity) { var placeOrderCommand = new PlaceOrderCommand(productId, quantity); _commandBus.Send(placeOrderCommand); // Other operations like sending notifications, updating metrics, etc. } else { throw new InvalidOperationException("Insufficient quantity available."); } } }
CQRS is used in this modified version of OrderApplicationService
to dispatch queries (such as GetProductQuery
and GetAvailableQuantityQuery
) to the query side, which retrieves the required data. Following that, a command (PlaceOrderCommand
) is sent to the command side via the command bus (ICommandBus
) to complete the order placement.
We may reap the benefits of both designs by integrating CQRS into Application Services. The separation of commands and queries into independent models improves scalability and efficiency, but Application Services remain a unified and elevated abstraction for managing complex use cases.