DDD : Can a repository access another repository?
Asked Answered
D

2

7

I am quit new to DDD. I have checked the responses for: Can a repository access another by DDD? prior to asking, but could not find any relevant information.

In my application, I have an object which contains another object as a property. I have a repository for the main object, but in order to retrieve the value for the property there is a need to access another repository.

My question is: should the first repository access the second repository, or should the application call the two repositories and merge them;

For instance:

for the two classes:

public class Foo1
{
    public int Id { get; set; }

    // .... More data

    public Foo2 foo2 { get; set; }
}

public class Foo2
{
    public int Id { get; set; }

    public int Type { get; set; }
}

Should the code look like:

public class Foo1Repository
{
    public Foo1 Get() { 
        return new Foo1();
    }
}

public class Foo2Repository
{
    public Foo2 Get(int foo1Id)
    {
        return new Foo2();
    }
}

public class Application
{
    public void main()
    {
        Foo1 foo1 = new Foo1Repository().Get();
        foo1.foo2 = new Foo2Repository().Get(foo1.Id);
    }
}

or more like:

public class Foo1Repository
{
    public Foo1 Get() {
        Foo2Repository foo2Repository = new Foo2Repository();
        Foo1 foo1 = new Foo1();
        foo1.foo2 = foo2Repository.Get(foo1.Id);

        return foo1;
    }
}

public class Foo2Repository
{
    public Foo2 Get(int foo1Id)
    {
        return new Foo2();
    }
}

public class Application
{
    public void main()
    {
        Foo1 foo1 = new Foo1Repository().Get();
    }
}

Sure, I would appreciate to know if there is any better architecture?

Thanks!

Domenech answered 23/2, 2020 at 16:2 Comment(4)
This question may be more appropriate for (and get a better response on) the Software Engineering stack exchangeServomechanical
@Domenech I don't think you need a specific repository for Foo2. In my view, you can simply create a method on Foo1's repository to retrieve such property, considering it is part of Foo1's domainConformal
@Padro , Thanks for the response. The thing is that Foo2 may be called individually from other part of the system. Meaning it is a business unit. In addition, this peace of information is stores in another provider as Foo1. Given that, so you still believe they belong in the same repository?Domenech
@Domenech According to your description, Foo2 seems to exhibit characteristics of an aggregate root, therefore needs its own repository. Then, however, the Foo1 entity must not reference Foo2 directly, by holding a field of type Foo2. Instead Foo1 must hold a field only for Foo2's identifier. The second case in my answer explains further.Risner
R
7

DDD recommends dedicating a repository for retrieval and storage of one aggregate root; aggregate roots are entities with a prominent role in the domain. Modifications of entities must happen through the aggregate root: retrieve an aggregate root with its repository, make necessary modifications, and store it back. Aggregate root's constituent entities must not be retrieved and modified independently, thereby no repositories for them.

In your case, depending on the context, Foo1 seems to be the aggregate root. Only one repository, for Foo1 should suffice; a repository for Foo2 is redundant. You can and must modify Foo1's property Foo2 through Foo1, after retrieving a Foo1 instance. Foo2 must not be retrieved and modified independently, thereby removing the need for a Foo2 repository.

However, again depending on the context, if you discover that Foo2 is an aggregate root itself, along with Foo1, only then it deserves its own repository. In that case, however, Foo1 must not hold a reference to Foo2 (that is a direct reference to another aggregate root, which is not recommended) and modify it in a transaction. Instead, it must refer to Foo2 by its identifier only, as shown below for your example:

public class Foo1
{
    public int Id { get; set; }

    // .... More data

    public int foo2Id { get; set; } // Foo2 identfier only, not a Foo2 instance
}

public class Foo2
{
    public int Id { get; set; }

    public int Type { get; set; }
}

If modification of Foo1 necessitates the same for Foo2, that must be done by retrieving Foo2 by its identifier that is held by Foo1, as the following example shows. Note that for simplicity the example modifies both aggregate roots in the same transaction. This kind of modification, however, usually happens in separate transactions that are eventually consistent.

Foo1 foo1 = foo1Repository.findById(foo1Id);

// modify foo1 state

Foo2 foo2 = foo2Repository.findById(foo1.foo2Id);

// modify foo2 state

// persist changes
foo1Repository.store(foo1);
foo2Repository.store(foo2);
Risner answered 23/2, 2020 at 17:36 Comment(0)
F
1

No, it should not. You are not fully implementing the repository concept as intended.

A repository is simply an interface to persist your DDD objects. There is no Ubiquitous Language requirement I can think of that'll require one repo to contain another. In other words, your subject matter expert will never say something like "The user should be saved to the flat file that's inside the database"; it doesn't make sense.

Your first example is closest to DDD. Note though, there's a subtle difference between a repository and a factory and strictly speaking newing up a domain object belongs in a factory or in a factory method that's part of an aggregate (this usually fits the UL better). Because repositories are meant to persist domain objects, they tend to interact a lot with Persistence Apis'(Like database Apis). So, if you need a repository to aggregate two databases for example, you'll need to interact with two different API's inside the repository and your consumer should know nothing about this detail.

Freshman answered 23/2, 2020 at 17:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.