4

I have a problem with regards to entity design. As I've read, when you design a DDD entity, the constructor should contain the values needed for an entity to "exist". For example, in the domain I am working on, a Class entity cannot exist without a Section and a Level:

public class Class
{
    public Class(short id, string section, Level level)
    {
        ID = id;
        Section = section;
        Level = level;
    }
    //
    // Properties
    //
    public short ID { get; private set; }
    public string Section { get; private set; }
    public Level Level { get; private set; }
    //
    // Methods
    //
    public static IList<Class> GetClassesByTeacher(short teacherID)
    {
        List<Class> classes = new List<Class>();
        classes.Add(new Class(1, "a", null));
        classes.Add(new Class(2, "b", null));
        classes.Add(new Class(3, "c", null));
        return classes;
    }
}

Here Level is also an entity. As I am not yet finished in the design, Level's contructor might also contain an entity SchoolYear. What bothers me is for me to call GetClassesByTeacher method, I need to instantiate a Class along with other entities (Level, and also SchoolYear, needed in Level's constructor).

Is this the correct? I think it's bothersome when I just want to call the method. Are there other ways? I considered making it static but others said testability would suffer. I'm not sure if CQRS is one of the solution for what I want to do as I haven't yet read too much about it, but if it is, are there any other techniques aside from CQRS I can employ, or is it when going DDD, this is how it really is? Or is my entity design incorrect?

g_b
  • 11,728
  • 9
  • 43
  • 80
  • 4
    Your design is perhaps not ideal. getClassesByTeacher belongs in a repository and not in Class. classes = classRepository->findByTeacher(teacherID or maybe teacher); – Cerad Jun 10 '14 at 16:10
  • @Cerad Ok I understand, but I thought from Service layer (or Presentation layer), we only reference Domain entities and the actual Domain entities are the ones using the repositories as needed and the also the ones returning results (in this case a list of Class)? Is this incorrect? – g_b Jun 11 '14 at 01:31
  • You can use both services and repositories in your service layer. – jgauffin Jun 12 '14 at 07:32
  • @jgauffin If an entity needs to access db for some business logic, should it just use DAL for this? I thought it should use the repository also. Thanks – g_b Jun 12 '14 at 08:43
  • your repository implementation is your dal. – jgauffin Jun 12 '14 at 08:59
  • @jgauffin I see, thanks. I find it confusing as some samples I have seen has a repository layer which then accesses a DAL. – g_b Jun 12 '14 at 09:05

2 Answers2

4

You should reconsider you domain model, as you actually say it seems there something wrong.

I may refer to Single Responsibility Principle (SRP) in which any class should have one one reason to change. In your example, what happens if we added a new field to 'Class', we will modify 'Class' it self and is right, but what happens if we decide that the listing should be on reversal order, or if you need a new type of list taking into account only interim teachers... you should change 'Class' but 'Class' has nothing to do with listings.

To answer you question, you might take a look at Repository pattern. Repositories are where you could ask for these type of listings.

I might split Class into two:

  • one for 'Class' model
  • other for 'Class' repositories

In summary:

public class Class
{
    public Class(short id, string section, Level level)
    {
        ID = id;
        Section = section;
        Level = level;
    }
    //
    // Properties
    //
    public short ID { get; private set; }
    public string Section { get; private set; }
    public Level Level { get; private set; }
}



public class ClassRepository
{
    private IList<Class> contents;

    //
    // Methods
    //
    public IList<Class> GetClassesByTeacher(short teacherID)
    {
        List<Class> classes = new List<Class>();
        for (Class elem: contents) {
            if (elem.getTeacher().equals(teacherID) {
                classes.Add(elem);
            }
        }
        return classes;
    }
}

repository = new ClassRepository;
level1 = new Level();
repository.Save(new Class(1, "a", level1));
repository.Save(new Class(2, "b", level1));
repository.Save(new Class(3, "c", level1));

result = repository.GetClassesByTeacher(1);

There are other details like using a ClassRepositoryInterface and implement with an InMemoryClassRepository, you will miss also Teacher information in class, as long as one one teacher drives the class, if not you might change how to filter by teacher, etc, etc.

I'm not a Java developer and the code will not compile but I expect you get the idea.

  • I thought from Service layer (or Presentation layer), we only reference Domain entities and the actual Domain entities are the ones using the repositories as needed and also the ones who will return the result (which in the sample code is a List of Class)? Is this incorrect? Is the Service Layer (or Presentation Layer if SL is not present) allowed to access these repositories? Thanks for the comment, much appreciated – g_b Jun 11 '14 at 01:28
  • 1
    @g_b: You got backwards. Domain entities should not be accessing repositories. It's the down stream layers that use repositories to access the entities. – Cerad Jun 11 '14 at 01:39
  • @Xavier OK, can you comment if this is correct: the Service Layer will access the repositories, which in turn references the Domain entities. OK, one question: if an Entity has a business rule that needs to access data in database for it to be processed, based on what I understand from what you typed, it should use the DAL directly, it shouldn't use a repository for this. Is this correct? – g_b Jun 11 '14 at 06:16
  • 1
    Sorry for not responding ASAP, In this case I suggest you to take a look at Specifications (http://martinfowler.com/apsupp/spec.pdf). A Specification is a class which responsibility is to know if an entity is valid from a Business Rules point of view. You could inject the required decencies to the Specification (Class Repository) and implement a method isSatisfied(Class elem) that returns true or false if an entity satisfies a given business rule. That means that the responsibility of knowing or validating your business rules for a given Class instance will be that Specification class. – Xavier Fornés Jun 14 '14 at 11:06
3

The GetClassesByTeacher method does indeed belong in some kind of repository or service class. Your Class entity is meant to represent an instance of that thing in the real world. An entity is not meant to provide instances (say, from some underlying persistence) - they are only meant to represent them. A ClassRepository would be a way to provide instances of the Class entity into your domain.

You also mentioned that Class cannot exist without a Level. You are speaking about aggregates here. There is a lot of DDD material online with regards to designing aggregates. Here are a couple:

EDIT:

When an entity needs another entity to exist, should it always be an aggregate?

No, just because entity A depends on entity B's existence, it doesn't mean that entity A needs to belong to entity B's aggregate.

As now I have a model in which many entities (like Class, Level, Subject, Adviser, Teacher, etc.) exists only in a certain SchoolYear (an entity). Is it OK for an aggregate to get that large?

An aggregate this large could result in performance and consistency issues.

  • Why performance? You could be loading a huge graph of data in memory for some aggregate root. You most likely only need to work on a fraction of that aggregate for whatever unit of work is occurring and not all the entities involved.
  • What consistency issues? The larger the aggregate, the more likely data can be changed in another part of the system while it has been retrieved into memory. When saving, data loss can occur.

Vaughn Vernon covers exactly these problems in his three-part Effective Aggregate Design series. It's a bit daunting if you aren't familiar with DDD lingo, but I highly recommend this read!

Also, by what you said, the Entity should not use the repository. If some business logic in the Entity needs database access for it to be processed, should it directly use DAL?

Entities should not know anything about repositories or services (or anything else, for that matter). They should not have any dependency on any other 'layer' in the system. Everything can know about your entities, but your entities shouldn't know about anything else. Provide me with an example of why you would want an entity to call on a repository and maybe I can provide a better answer.

Dave New
  • 38,496
  • 59
  • 215
  • 394
  • @davenzawa Some questions: When an entity needs another entity to exist, should it always be an aggregate? As now I have a model in which many entities (like Class, Level, Subject, Adviser, Teacher, etc.) exists only in a certain SchoolYear (an entity). Is it OK for an aggregate to get that large? If so, how would I go about performance as for example, to get to a teacher, if in an aggregate, I will need to go to SchoolYear -> Level -> Class just to get a reference to the Teacher. – g_b Jun 12 '14 at 08:37
  • @davenzawa Also, by what you said, the Entity should not use the repository. If some business logic in the Entity needs database access for it to be processed, should it directly use DAL? – g_b Jun 12 '14 at 08:41
  • @davenzawa One more thing about aggregates, for example SchoolYear. With aggregates, this means everytime I need a Level, I should get it from a SchoolYear. When SchoolYear is created, does it necessarily have to contain all Levels even if I need only one Level? – g_b Jun 12 '14 at 09:49
  • @davenzawa About the entity and repository thing, I was just thinking that I might arrive in a situation where a business rule might need to access some info in database before it can be processed, but maybe it'll get clearer when I actually get into that situation. I'll open a new question by then. Thanks! – g_b Jun 15 '14 at 01:19