3 things to avoid when implementing Domain-Driven Design (DDD)
Tags: ddd, refactoring, csharp, designpatterns
Published: 2023-06-07 17:08:09

The reason of this post is due to the fact I often see developers trying to follow Domain-Driven Design (DDD) approach but make very questionable decisions **during the implementation**. 

Here is a summary of things which should be avoided: 

## 1. Public setters

Let's look at this simplified entity (skipped other properties):

```csharp
public class Order
{
   public PaymentMethod PaymentMethod { get; set; }
   public decimal Total { get; set; }
}
```

**What's wrong with it?**: The entity should encapsulate both data and behaviour. Having a setter means anyone can modify the property which allows to change its state.

Let's check this example with few enforced rules:
1. Order can't exist without payment method or order total
2. We can't place order with 0 or negative total
3. We can't place cash orders for orders with total value over 100 (whatever the currency)

```csharp
public class Order
{
    public PaymentMethod PaymentMethod { get; private set; }
    public decimal Total { get; private set; }

    private static decimal CashOrderMaxTotal = 100;

    public Order(PaymentMethod paymentMethod, decimal orderTotal)
    {
        if (orderTotal == 0 || orderTotal < 0)
        {
            throw new ArgumentException(String.Format("{0} can't be 0 or negative", orderTotal), "orderTotal");
        }

        if (paymentMethod.Cash && orderTotal > CashOrderMaxTotal)
        {
            throw new ArgumentException(String.Format("Unable to order total {0}, cash order cannot total more than {1}", orderTotal, CashOrderMaxTotal), "orderTotal");
        }

        Total = orderTotal;
        PaymentMethod = paymentMethod;

    }
}
```

To enforce **rule 1**  - we use a **constructor** to set properties and to enforce **rule 2 and 3** we add **validation**.

## 2. Lack of rules validation

Let's take a look again:

```csharp
public class Order
{
    public decimal Total { get; private set; }

    public Order(decimal orderTotal)
    {
        Total = orderTotal;
    }
}
```

**What's wrong with it?**: As seen on the previous example, lack of validation rules means anyone can set this order to some unwanted values which is not good and can make its way to big fireworks of bugs. Always code defensively and **never let the entity to go into invalid state**. 

Here is an example how one would make sure our order total is valid. For this purpose I use an amazing library called [GuardClauses](https://github.com/ardalis/GuardClauses) which is just shorter way of throwing an argument exception if the condition is not met.

```csharp
public class Order
{
    public decimal Total { get; private set; }

    public Order(decimal orderTotal)
    {
        Guard.Against.NegativeOrZero(orderTotal);
        Total = orderTotal;
    }
}
```
The important part here is, the code that creates order or modifies it in any sense should be validated and rejected on any bad intention. 

## 2. Adding Persistence Logic

Now let's persist our order: 

```csharp
[Table("Orders")]
public class Order
{
    public decimal Total { get; private set; }
}
```

**What's wrong with it**: By adding `[Table("Orders")]` which is Entity Framework data annotation we are mixing persistence stuff within entities, we're violating the Single Responsibility Principle and we could lead to tightly coupled code that's harder to maintain and test.

This post doesn't focus on the **design**. Domain-Driven Design covers more concepts such us defining the domain, aggregate roots, value objects and using ubiquitous language. I might cover it in the next post. :fire:

To read more about Domain Driven Design I recommend the best book out there: 

[Domain Driven Design by Eric Evans](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215?&_encoding=UTF8&tag=kedzior-20&linkCode=ur2&linkId=46ec3170ffe91066990bf2575c01f3c2&camp=1789&creative=9325).