DDD: il metodo dell'entità dovrebbe utilizzare il repository per le stored procedure (non CRUD)?

2

Immagina di avere un'entità Veicolo nel tuo modello di dominio. L'entità veicolo ha il metodo Riserva che mette il veicolo nello stato "riservato" e fa un'altra cosa. Ma il metodo Reserve deve prima fare dei controlli per assicurarsi che la prenotazione possa essere fatta. Questo controllo viene eseguito da una stored procedure legacy che deve essere chiamata come parte del processo di prenotazione. La chiamata di stored procedure è incapsulata nel metodo di repository.

La domanda: Devo passare il repository come parametro del metodo dell'entità di dominio se il metodo interagisce con l'archiviazione dei dati? Ci sono degli svantaggi di tale soluzione? Ci sono alternative?

L'esempio:

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 ...
        }
    }
}
    
posta Philipp Bocharov 20.08.2017 - 14:47
fonte

2 risposte

3

Il vantaggio di avere il Metodo sulla classe del veicolo è che corrisponde alla tua lingua commerciale. "Voglio prenotare il veicolo per favore!"

Lo svantaggio è che ogni volta che si dispone di un veicolo, è necessario avere anche il servizio dipendente in giro nel caso in cui si desidera prenotare.

Se si può refactoring fuori dal sproc in modo che sia solo una operazione logica sui membri del veicolo grande.

Tuttavia, se non è possibile, poiché l'operazione si basa su informazioni esterne al Veicolo, come la conoscenza di tutti gli altri veicoli, è possibile prendere in considerazione una classe VehicleReservationService alla quale si passa l'oggetto Veicolo.

Dopo tutto, in realtà stai solo nascondendo l'esistenza di questo servizio con Vehicle.Reserve (repository di IDependency). A volte la lingua del business è sbagliata e deve cambiare,

Chiarimento:

No. Non dovresti passare il repository. O spostare il codice dal db se può essere reso una funzione pura del veicolo. Oppure crea un VehicleResevationService per gestire le prenotazioni.

Esempio:

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);
    }
}
    
risposta data 20.08.2017 - 15:10
fonte
2

Personalmente non sono in un gruppo di persone che pensano che Entities non debba accedere a Repositories . Quindi per me, la risposta alla tua domanda è "Sì, certo, vai avanti".

Ma nel caso in cui tu non voglia farlo, vorrei cambiare leggermente la soluzione di Ewan.

// 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
        }
    }
}

Rendendo il RegistrationService nidificato di Vehicle , gli consente di accedere ai suoi membri privati. Questo perché rendere il servizio annidato all'entità li accoppia insieme. Quindi il servizio è parte integrante dell'entità. È ancora possibile utilizzare DI per creare il ReservationService e averlo separato in questo modo rende chiaro quali operazioni fanno uso del servizio di repository.

Un'altra cosa da sottolineare è che invece di usare generico IVehicleRepository , ho creato uno speciale IVehicleReservationRepository , che è usato solo in questo caso d'uso. Alla fine, potrebbe essere implementato su VehicleRepository , ma questo non è correlato a questo problema. Rendere l'interfaccia specifica per un caso d'uso è un buon utilizzo del principio di segregazione dell'interfaccia e renderebbe il test più semplice, poiché non è necessario preoccuparsi di altri metodi presenti nel repository completo.

L'ultima cosa che mi è venuta in mente è lo scenario, dove potresti voler prenotare più veicoli. Se avevi il metodo di riserva su un'entità, dovresti chiamarlo su ogni veicolo, chiamando il repository, e quindi il database, per ogni entità. Ma avere un servizio separato consente di ottimizzare questo chiamando il deposito una volta per più veicoli. Come visto nel metodo ReserveAll su ReservationService .

    
risposta data 20.08.2017 - 21:02
fonte

Leggi altre domande sui tag