Search code examples
ormdomain-driven-designentityrepository-patternddd-repositories

DDD - Access repositories from entities, bis


I've asked a previous question last month, about whether an Entity should access a Repository, and although it looks like most people think they shouldn't, I have to admit it's hard for me to get convinced.

I have a use case for which I really can't think of any (reasonable) way to do the logic without injecting the Repository in my Entity:

We have a Store, which is assigned a Zone (city, district, ... - user defined). To reduce the workload of the employee in charge of adding Stores to the database, and to ensure consistency, we don't ask him to select the Zone in which he wants to add the Store. He just zooms on a map, clicks to pinpoint the Store location, and saves. The application then has to find the most relevant Zone for this location.

What I currently have is something like:

class Store
{
    protected Zone zone;
    protected Point location;
    protected ZoneRepository zoneRepository;

    public void setLocation(Point location)
    {
        Zone matchingZone = this.zoneRepository.findByLocation(location);
        if (matchingZone == null) {
            throw new ApplicationException(
                "The store location must be within a zone boundary");
        }
        this.location = location;
        this.zone = matchingZone;
    }
}

Do you have any solid alternative which would support the commonly accepted opinion that this design is inherently bad?


Solution

  • From the description you've provided it seems that Point is actually not part of your Ubiquitous Language - the Zone is. You don't set a point for store - you assign a Zone to it. In other words, the signature of this operation shouldn't be setLocation(Point location) - it should be assignZone(Zone locationZone). The translation between point selected by user and the zone should occur before domain model operation is performed. That's how I'd model it.

    Example added after comments (C# if you don't mind - and shows only a concept). Code assumes command-based approach to performing user actions - that's how I tend to do it, but might be an Application Service or Controller even, depending on the structure of your application.

        public class StoreLocationHandler : Handles<LocateStoreCommand>
        {
            public void Handle(LocationStoreCommand command)
            {
                // Location contains coordinates as well as zone info and can be obtained only via LocationService
                Location location = LocationService.IdentifyZone(command.Coordinates); 
    
                Store store = StoreRepository.Get(command.StoreId);
    
                store.AssignLocation(location);
    
                // persist changes - either by Unit of Work or Repository
            }
        }
    

    The point is that you don't have to craete everything inside your domain entities - Zone seems to be Value Object from perspective of Store. Besides, further separation of those two concepts might lead to additional possibilities, like identifying the zone not online but through some kind of background process (for the sake of performance or scalability). In addition, in my opinion it fits better considering Dependency Injection principle.

    After all, domain doesn't care about how the zone is created or retrieved, it cares about association between Store, its location and the zone it falls into. Why should it be responsible for translation between a point and a zone? At least that's the way I see it.