1.4 Domain Application in Domain-Driven Design (DDD)
Introduction
Domain application is the important step in Domain-Driven Design (DDD) where we change our domain model into real code. In this article, we will look at how to use main DDD ideas in C#. We will focus on entities, values, domain services, and how to organize them in aggregates.
Basic Concepts and How to Use Them
1. Value Objects
Value objects are things with no identity and only defined by their value. In C#, we can use record to make value objects good.
public record Money
{
public decimal Amount { get; init; }
public string Currency { get; init; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
}
Using record makes sure the object can't change and gives value comparison automatically.
2. Entities
Entities are objects with unique identity and state that can change.
public class Product
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Money Price { get; private set; }
public Product(string name, Money price)
{
Id = Guid.NewGuid();
Name = name;
Price = price;
}
public void UpdatePrice(Money newPrice)
{
if (newPrice.Amount < 0)
throw new ArgumentException("Price cannot be negative");
Price = newPrice;
}
}
See how Id is made inside and how UpdatePrice follows business rules.
3. Aggregates
Aggregates are groups of entities and values we treat as one unit to keep data consistent.
public class Order
{
public Guid Id { get; private set; }
private List<OrderItem> _items;
public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();
public Money TotalAmount { get; private set; }
public Order()
{
Id = Guid.NewGuid();
_items = new List<OrderItem>();
TotalAmount = new Money(0, "USD");
}
public void AddItem(Product product, int quantity)
{
var item = new OrderItem(product, quantity);
_items.Add(item);
RecalculateTotal();
}
private void RecalculateTotal()
{
decimal total = 0;
foreach (var item in _items)
{
total += item.Product.Price.Amount * item.Quantity;
}
TotalAmount = new Money(total, "USD");
}
}
public class OrderItem
{
public Product Product { get; private set; }
public int Quantity { get; private set; }
public OrderItem(Product product, int quantity)
{
Product = product;
Quantity = quantity;
}
}
Order is the aggregate root and makes sure all OrderItems connected to it are consistent.
领英推荐
4. Domain Services
Domain services have business logic that doesn't fit well in a specific entity or value.
public class OrderService
{
private readonly IProductRepository _productRepository;
public OrderService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public Order CreateOrder(List<(Guid productId, int quantity)> orderItems)
{
var order = new Order();
foreach (var item in orderItems)
{
var product = _productRepository.GetById(item.productId);
if (product == null)
throw new ArgumentException($"Product with id {item.productId} not found");
order.AddItem(product, item.quantity);
}
return order;
}
}
This service coordinates making an order using information from the repository.
5. Repositories
Repositories give an interface for storing and getting entities.
public interface IProductRepository
{
Product GetById(Guid id);
void Save(Product product);
}
This interface can be implemented using a real database in the infrastructure layer.
Using Concepts in a Real Scenario
Let's say we building a simple e-commerce system. We will implement the process of creating a new order:
public class ECommerceApplication
{
private readonly OrderService _orderService;
private readonly IProductRepository _productRepository;
public ECommerceApplication(IProductRepository productRepository)
{
_productRepository = productRepository;
_orderService = new OrderService(productRepository);
}
public Order PlaceOrder(List<(Guid productId, int quantity)> items)
{
var order = _orderService.CreateOrder(items);
// Here we can add more logic like checking stock, calculating taxes, etc.
return order;
}
}
In this example, ECommerceApplication represents the application layer that coordinates between the user interface and domain services.
Conclusion
Using DDD concepts in C# needs deep understanding of both the language and the domain. By using entities, values, aggregates, and domain services correctly, we can make strong and maintainable applications that accurately show our domain model.
Remember that these examples are just a starting point. In a real application, you will need to handle more complexities like validation, error handling, and integration with external systems.
The key is to keep focus on the domain and make sure the code clearly shows the main concepts and processes in your business domain.
Business Line Manager at SQLI
3 周Thank you, Nabil for this tutorial ! We just discussed our expertise in DDD during the presentation of the Business APP offering, as well as our capabilities at ISCM to support our clients in transforming their legacy applications, particularly in moving to a microservice architecture. I believe that addressing our use cases with some clients and the challenges we faced would be also an excellent way to highlight our expertise in this area...
Team leader , Full-stack .Net Developer | Microsoft Certified x4
3 周Interesting