1

I've read that accessing repository from aggregate root considered bad practice. If it is, than consider following example:

class User {
   private String username;
   public void changeUsername(String newUsrname) {
     // How will I persist username to database if I don't have access to repository from aggregate root?
     ...
   }
}

How will I persist username to database if I don't have access to repository from aggregate root?

I see it like this:

class User {
    private String username;
    private UserRepository userRepository;
    public User(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void changeUserName(String newUsername) {
       this.username = newUserName;
       userRepository.save(this); 
    }
}

Or I've missed something in DDD concepts?

Teimuraz
  • 8,795
  • 5
  • 35
  • 62
  • 1
    your example shows ActiveRecord not DDD. – alex Oct 06 '16 at 12:41
  • @dit, how would you achieve this in DDD way? – Teimuraz Oct 06 '16 at 13:07
  • check this DDD-example: https://github.com/citerus/dddsample-core – alex Oct 06 '16 at 13:29
  • @dit, From example you provided, I should introduce UserService, in that userService I should have UserService.changeUserName(userId, username) which will get user from repository, call user.changeUsername and service will store user back... So interaction with domain should be don through services? And what's wrong with my approach? – Teimuraz Oct 06 '16 at 13:46
  • Repository has methods like `getById`, `update(user)`, `delete(user)` and implements no logic, just CRUD operations. The logic will be implemented in a Service. – alex Oct 06 '16 at 14:24
  • Possible duplicate of [Is it ok for entities to access repositories?](http://stackoverflow.com/questions/827670/is-it-ok-for-entities-to-access-repositories) – guillaume31 Oct 10 '16 at 09:02

2 Answers2

7

How will I persist username to database if I don't have access to repository from aggregate root?

Current practices normally handle I/O in the application component, rather than in the domain model.

Application {
    void when(ChangeUserName command) {
        User user = this.userRepository.getUserById(command.userId);
        user.changeName(command.name);
        this.userRepository.save(user);
    }
}

Recommended reading: Vladimir Khorikov on domain model isolation.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • It's Vlad Khononov, I'm reading his book right at this moment :P – Ced Jul 07 '22 at 20:08
  • 1
    Vlad Khononov (author of _Learning Domain Driven Design_) and Vladimir Khorikov (author of Unit Testing Principles, Practices, and Patterns) are different people. – VoiceOfUnreason Jul 08 '22 at 01:20
  • This is a nice example. I have one question, why would I do this when I can have one method (e.g `updateUserName(newName string)`) in my repository which updated the DB directly using SQL and then I won't have to pull out the User object from the DB? – OhhhThatVarun Jul 24 '23 at 18:21
0

Just to extend a little the answer given by @VoiceOfUnreason (which is totally right) here a quick explanation on why injecting repositories via constructor to Aggregate Roots is not advisable:

The repository should depend on the object it returns, not the other way around. The reason for this is that your "domain object" (more on that later) can exist (and should be testable) without being loaded or saved (that is, having a dependency on a repository).

Basically your design says that in order to have an user, you need to provide a MySQL/Mongo/XXX instance connection which is an infrastructure detail. Your domain should not know anything about how it is persisted. Your domain knows about the behavior, invariants, business rules and so on.

These concepts just help you to create code easier to maintain as well as help you to apply best practices such as SRP (Single Responsibility Principle).

mgonzalezbaile
  • 1,056
  • 8
  • 10
  • In this case, domain will not now anything about infrastructure, because I'm passing just repository interface, and repository interface is a part of domain. – Teimuraz Oct 07 '16 at 00:17
  • Just think about the unit test of the User class. Say that in order to change the name of the user you have some invariants like it cannot be empty, nor longer than a certain length, etc. Ensuring the invariants is what your User AR is responsible for, but if you also adds the responsibility of persisting, fetching and so, you are breaking SRP making harder the maintenance of the AR class plus the unit tests. – mgonzalezbaile Oct 08 '16 at 09:33