Skip to main content

Domain model entity layer depends on Persistance layer here? [Resolved]

I am trying to apply Domain-Driven Design here in this bank ATM mock app.

I have 3 layers at the moment:

  • Domain Model Entity layer (Pure POCO classes)
  • Persistence layer (Entity Framework)
  • Console UI layer

In Persistence layer, I have RepositoryTransaction class which perform CRUD operation.

namespace ATM.Persistance.Repository
{
public class RepositoryTransaction
{
    private readonly AppDbContext db;

    public RepositoryTransaction()
    {
        db = new AppDbContext();
    }

    public int AddTransaction(Transaction transaction)
    {
        db.Transactions.Add(transaction);
        return db.SaveChanges();
    }
}
}

In my Domain Model Entity layer, I have BankAccount class.

public class BankAccount
{
    public int Id { get; set; }
    public string AccountName { get; set; }
    public decimal Balance { get; set; }

    // To create RepositoryTransaction to call AddTransaction method.
    // But according to Domain-Driven Design, domain model layer should not depend on
    // any layer, right? But here, it depends on Persistance layer.
    // If it doesn't depend on Persistance layer, then where do I do so? In Console UI Core?
    // private readonly RepositoryTransaction repoTrans = new RepositoryTransaction();

    public decimal CheckBalance()
    {
        return Balance;
    }

    public void Deposit(int amount)
    {
        // Domain logic
        Balance += amount;

        // Add transaction
        var transaction = new Transaction()
        {
            Id = 1,
            TransactionDateTime = DateTime.Now,
            Amount = amount,
            TransactionType = TransactionType.Deposit
        };

        // repoTrans.AddTransaction(transaction);
    }

    public void Withdraw(int amount)
    {
        // Domain logic
        Balance -= amount;

        var transaction = new Transaction()
        {
            Id = 2,
            TransactionDateTime = DateTime.Now,
            Amount = amount,
            TransactionType = TransactionType.Withdraw
        };

        // repoTrans.AddTransaction(transaction);
    }
}

and in Console UI layer, I have the Program class and the Main method:

namespace ATM.ConsoleUICore
{
class Program
{
    static void Main()
    {
        var bankAccount = new BankAccount() { Id = 1, AccountName = "John", Balance = 250M };

        Console.WriteLine("1. Check balance");
        Console.WriteLine("2. Deposit");
        Console.WriteLine("3. Withdraw");
        Console.WriteLine("Enter option: ");
        string opt = Console.ReadLine();
        switch (opt)
        {
            case "1":
                Console.WriteLine($"Your balance is ${bankAccount.CheckBalance()}");
                break;
            case "2":
                // User to input amount.
                // Data validation to make sure amount is greater than zero.

                bankAccount.Deposit(50);
                Console.WriteLine("Deposit successfully");
                break;
            case "3":
                // User to input amount.
                // Data validation to make sure amount is greater than zero.

                bankAccount.Withdraw(20);
                Console.WriteLine("Withdraw successfully");
                break;
            default:
                break;
        }

    }
}
}

My question is written in the BankAccount class. According to Domain-Driven Design, domain model layer should not depend on any layer, right? But here, it depends on Persistence layer. If it doesn't depend on Persistence layer, then where do I do so? In Console UI layer?


Question Credit: Steve
Question Reference
Asked August 24, 2019
Posted Under: Programming
20 views
3 Answers

public void Deposit(int amount)
{
    // ...
    // repoTrans.AddTransaction(transaction);
}

This is the part of your code that is a bit weird, in terms of Domain Driven Design.

The domain model is usually composed of pure in-memory representations of your domain; they own their own data structures. So you either send them information so that they may modify themselves, or you ask them questions.

If transactions are part of the BankAccount; meaning that they are always stored and processed together, then you would normally see logic like

public void Deposit(int amount)
{
    // ...
    this.AddTransaction(transaction);
}

And the transaction would be written to your persistence store as part of the bank account (aka via the bank account "repository").

On the other hand, if transactions are a separate thing from Bank Accounts, then you are more likely to see code like

public Tranasaction Deposit(int amount)
{
    // ...
    return transaction;
}

Or, if the bank account really is just contributing information, then you might see something more like

Transaction transaction = new Transaction(int amount, Id<BankAccount> accountNumber);

As written in your example, we would expect the transaction to be part of the bank account itself -- it would not have its own repository, because it is saved as part of the BankAccount entity.

Broadly - the domain model doesn't know anything about persistence concerns like repositories or transactions. The domain model just implements the bookkeeping of the domain; the application layer is responsible for figuring out when to have information copied from transient storage to durable storage.


credit: VoiceOfUnreason
Answered August 24, 2019

I will only comment on the design and the code from the perspective of DDD. Specifically, using the free DDD Reference by Eric Evans, pp. 9, 10 and 17 (layered architecture and repositories).

According to Domain-Driven Design, domain model layer should not depend on any layer, right?

I think you incorrectly interpreting a statement like this:

Isolate the expression of the domain model and the business logic, and eliminate any dependency on infrastructure, user interface, or even application logic that is not business logic.

I think the author is mostly concerned about the risk that a lot of technical details of interaction with storage technologies leak into the domain layer.

The author then clarifies:

Concentrate all the code related to the domain model in one layer and isolate it from the user interface, application, and infrastructure code... The key goal here is isolation.

The goal of DDD is to

create a better software by focusing on the domain model rather than the technology.

The key obstacle in this context is:

When the domain-related code is diffused through such a large amount of other code, it becomes extremely difficult to see and to reason about.

On the topic of repositories Evans suggests:

create a service that can provide the illusion of an in-memory collection of all objects of that aggregate’s root type. Set up access through a well-known global interface. Provide methods to add and remove objects, which will encapsulate the actual insertion or removal of data in the data store. Provide methods that select objects based on criteria meaningful to domain experts...

So putting it all together my conclusions are:

  • Your code is completely fine from DDD POV.

  • You properly established a layered "architecture". Each layer depends only on the underlying ones.
  • You clearly separated business logic from UI, persistence and infrastructure. (Though I am not sure, if your solution is practical: you need in the end to persist somehow the balance).
  • You should not go out of their way to pretend that persistence does not exist. Instead, you should focus on keeping the business logic clearly expressed, localized in domain layer and not interleaved with UI/infrastructure concerns. For that you can use Repository pattern to provide a simple persistence interface to domain layer. The challenge is to prevent each of the concerns (business logic and persistence) from leaking into the other domains.


credit: Alexey
Answered August 24, 2019
Your Answer