2

Imagine you have a Vehicle entity in your domain model. Vehicle entity has Reserve method that put vehicle in "reserved" state and do another stuff. But Reserve method have to do some checking first to ensure that reservation could be done. This checking is done by legacy stored procedure that have to be called as a part of reservation process. Stored procedure call is encapsulated in repository method.

The question: Should I pass repository as a parameter of domain entity method if method interacts with data storage? Are there any drawbacks of such a solution? Are there alternatives?

The sample:

class VehicleRepository: IVehicleRepository {
    public bool IsReserveAvailable() {
        // call stored proc here
    }
}

class Vehicle {
    public void Reserve (IVehicleRepository vehicleRepository) {
        bool isReserveAvailable = vehicleRepository.IsReserveAvailable();
        if (isReserveAvailable) {
            // do stuff ...
        }
    }
}
  • What kind if checks are done by the stored procedure? – Constantin Galbenu Aug 20 '17 at 13:41
  • And what logic is done by the Reserve method. So far seems to me a violation of responsabilities allowing the vehicle to know how to make a reserve. – Laiv Aug 20 '17 at 13:51
  • 2
    Possible duplicate of Accessing Repositories from Domain – Euphoric Aug 20 '17 at 14:57
  • Take a look to this question – Laiv Aug 20 '17 at 15:45
4

The advantage of having the Method on the vehicle class is that it matches your business language. "I want to reserve the vehicle please!"

The disadvantage is that whenever you have a vehicle, you have to also have the dependent service around in case you want to reserve it.

If you can refactor it out of the sproc so that it is only a logic operation on the members of Vehicle great.

But if you can't, because the operation relies on information outside of the Vehicle, such as knowledge of all other vehicles, then you might want to consider a VehicleReservationService class which you pass the Vehicle object to.

After all, you are really only hiding the existence of this service with Vehicle.Reserve(IDependency repo). Sometimes the business language is wrong and needs to change,

Clarification:

No. You should not pass the repository. Either move code out of the db if it can be made a pure function of Vehicle. Or create a VehicleResevationService to deal wth reservations.

Sample:

public class Vehicle
{
    public bool Reserve()
    {
        if(this.x && this.y)
        {
            this.Status = "reserved";
            return true;
        }
        return false;
    }
}

public class VehicleReservationService
{
    public VehicleReservationService(IRepository repo)
    {
        this.repo = repo;
    }

    public bool Reserve(Vehicle v)
    {
        return repo.Reserve(v.Id, v.OtherParametersOfSproc);
    }
}
  • 1
    "I want to reserve the vehicle please!"... Do say it to the car or to someone else? The car doesn't know anything about the booking process... – Laiv Aug 20 '17 at 13:41
  • I have added a clarification – Ewan Aug 20 '17 at 14:55
  • I still miss something here. Probably, Vehicle repository should not perform business logic either. Don't you think that a cross-cutting conocept is missing? Reservation for example. It's a "bounded context" in its own rigth. – Laiv Aug 20 '17 at 17:03
  • Well @Ewan give pretty explanatory answer. My only concern is that huge use of stored procedures will cause moving logic from entity to domain services. Domain model will tend to be more anemic that is considered by some developers (not me) as anti-pattern. – Philipp Bocharov Aug 20 '17 at 17:48
  • To reduce the anemic feeling, you could put VehicleReservationService into Vehicle class itself. This will make the service integral part of the entity, thus allow the service to access private fields and methods of it's parent entity. While at the same time being able to properly DI the service itself. – Euphoric Aug 20 '17 at 18:28
2

I'm personally not in a group of people who think that Entities should not access Repositories. So to me, the answer to your question is "Yes, sure, go ahead."

But in case you don't want to do that, I would like to change Ewan's solution slightly.

// notice this is not general vehicle repository
public interface IVehicleReservationRepository
{
    bool IsReserveAvailable();
}

public class Vehicle
{
    // notice Reserve being private
    // should only be called when reservation can happen
    private void Reserve()
    {
        // do stuff ...
    }

    public class ReservationService
    {
        private IVehicleReservationRepository _vehicleReservationRepo;

        public ReservationService(IVehicleReservationRepository vehicleReservationRepo)
        {
            _vehicleReservationRepo = vehicleReservationRepo;
        }

        public void Reserve(Vehicle v)
        {
            bool isReserveAvailable = _vehicleReservationRepo.IsReserveAvailable();
            if (isReserveAvailable)
            {
                v.Reserve();
            }
        }

        public void ReserveAll(IEnumerable<Vehicle> vehicles)
        {
            // efficient reservation for multiple vehicles
            // doesn't need to call repository for every vehicle
        }
    }
}

By making the RegistrationService nested of Vehicle, it allows it to access it's private members. This is because making the service nested to the entity couples them together. So the service is integral part of the entity. It is still possible to use DI to create the ReservationService and having it separate like this makes it clear what operations make use of the repository service.

Another thing to point out is that instead of using generic IVehicleRepository, I created special IVehicleReservationRepository, that is only used in this use case. In the end, it might be implemented on VehicleRepository, but that is unrelated to this problem. Making interface specific for one use case is good usage of interface segregation principle and would make testing easier, as you don't need to worry about other methods present on full repository.

Last thing that came to my mind is scenario, where you might want to reserve multiple vehicles. If you had Reserve method on a Entity, you would have to call it on each vehicle, calling repository, and thus database, for every entity. But having separate service allows you to optimize this by calling repository once for multiple vehicles. As seen in ReserveAll method on the ReservationService.

  • would you mind to elaborate the benefits of allowing entities to meet their repositories? That reminds me to Active Record design pattern. Despite it's considered anti-pattern, I have found libs that implement it. So I'm missing the Pros. The cons I think are clear. – Laiv Aug 20 '17 at 20:05
  • @Laiv The critical difference between Active Record and entity being able to access it's repository is that, with Active Record, it is not possible to structurally separate the implementation of repository (eg. the SQL commands) from the entity/record. But having a middle man, like an interface, does make that possible. So for example, unit testing of entities, in isolation from persistence technology, becomes possible. Which is, I think, main complain when using Active records. – Euphoric Aug 20 '17 at 20:15
  • ok. From the unit-testing standpoint, your approach is preferable. But what's the point of allowing entities to meet the DAL? Maybe I'm too oldschool or biased by too many years abstracting the model from everything else. – Laiv Aug 20 '17 at 20:22
  • @Laiv Can you explain what you mean by DAL? Also, do you think domain should have abstraction of "collection of all existing entities" within itself? If yes, how is this abstraction represented? If not, how would domain express behavior that works on all existing entities? And by all existing entities I mean for example "all current vehicles". – Euphoric Aug 20 '17 at 20:27
  • Ok, ok. I think I got your point now. Basically you are looking at the repository as the collection of the existing vehicles while I'm still looking at it as a DAO. But still, should a single entity to be aware of everyone else (should a vehicle to have access the collection?) I guess the answer is no, and that's why you suggested the interface segregation, in order to don't force vehicles to be aware of the collection itself. I'm right? – Laiv Aug 20 '17 at 20:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.