DDD - Regola aziendale che dipende da un servizio

1

Mi sono confuso se è necessario un dominio per accedere a un servizio esterno in alcuni stati del suo ciclo di vita.

La regola aziendale è questa:

If an Employee is accepting a JobSeeker's JobApplication, then the Employee must create an Appointment and have it e-mailed to the JobSeeker.

Le mie soluzioni attuali sono:

1 - Lascia la responsabilità di invio al dominio JobApplication

class JobApplication {
    public function makeAppointment(IMailService $mailService, IMailGenerator $mailGen, \DateTime $time, Address $address = null) {    
        $employer = $this->getVacancy()->getEmployer();

        $this->accept();

        $appointment = new Appointment($employer,
            $this->applicant,
            'Interview Invitation - '.$employer,
            $time,
            $address ?: $employer->getAddress()
        );

        $mailService->send(
            $appointment->getJobSeeker()->getMail(), 
            $appointment->getTitle(),
            $mailGen->generateAppointmentMail($appointment)
        );

        return $appointment;
    }    
}

Mi sento in imbarazzo con questa opzione, poiché lascia l'entità JobApplication a dipendere dai servizi di Mailing.

2 - Crea un nuovo AppointmentService

class AppointmentService {
    private $mailService;
    private $mailGen;

    public function __construct(IMailService $mailService, IMailGenerator $mailGen) {
        $this->mailService = $mailService;
        $this->mailGen = $mailGen;
    }

    public function makeAppointmentFromJobApplication(JobApplication $application, \DateTime $time, Address $address = null) {
        $jobApplication->accept();

        $appointment = new Appointment($application->getCompany(),
            $application->getApplicant(),
            'Interview Invitation - '.$application->getCompany(),
            $time,
            $address ?: $application->getCompany()->getAddress()
        );

        $mailService->send(
            $appointment->getJobSeeker()->getMail(), 
            $appointment->getTitle(),
            $mailGen->generateAppointmentMail($appointment)
        );

        return $appointment;
    }
}

Questa opzione è utile poiché ho solo bisogno di iniettare AppointmentService sul controller, ma lascia la classe Appointment anemica.

Una volta ho letto per non lasciare mai il dominio dipendente dai servizi di dominio, l'altro modo è corretto. Tuttavia, alcune regole aziendali richiedono servizi esistenti nell'ambito del dominio.

Ci sono altre opzioni?

Aggiornamento - 1

Una terza opzione sarebbe come questa:

class Employer {
    public function acceptApplication(JobApplication $application) {
        if($application->isRejected()) throw new \Exception('Cannot make appointment for rejected application');

        $application->setAccepted();
    }

    public function makeAppointment(IAppointmentService $appointmentService, JobApplication $application, \DateTime $time, Address $address = null) {
        if(!$application->isAccepted()) $this->acceptApplication($application);

        return $appointmentService->makeAppointment($this, $application->getApplicant(), $time, $address ?: $this->getAddress());
    }
}

class AppointmentService {
    private $mailService;
    private $mailGenerator;

    public function makeAppointment(Employer $company, JobSeeker $jobSeeker, \DateTime $time, Address $address = null) {
        $appointment = new Appointment($company, 
            $jobSeeker,
            'Interview Invitation - '.$application->getCompany(),
            $time,
            $address
        );

        $this->mailAppointment($appointment);

        return $appointment;
    }

    private function mailAppointment(Appointment $appointment) {
        return $mailService->send($appointment->getJobSeeker, $appointment->getTitle, $mailGenerator->generateApplicationAppointment());
    }
}
    
posta Samuel Adam 15.04.2014 - 07:19
fonte

1 risposta

3

La regola non dipende dall'infrastruttura, ma puoi creare un'interfaccia in termini di business per comunicare con tale infrastruttura. Questi sono i tuoi servizi.

L'invio di posta è un requisito aziendale, o servizio, ma la modalità di invio della posta dipende da te. Potresti voler creare la tua interfaccia di posta aziendale specifica, e una delle implementazioni di potrebbe collegare qualsiasi libreria di posta che stai usando.

Detto questo, date le tue due opzioni, penso che il n. 2 sia più corretto.

Uno dei punti principali a cui hai alluso è quello di separare i dati dal comportamento. Dovresti passare i dati, e in genere i servizi sono delle dipendenze piuttosto che degli oggetti di business passati.

Tuttavia, il design attuale si sente ancora troppo concentrato sui servizi piuttosto che sull'oggetto business. Nulla sulla conversione di un'applicazione di lavoro in un appuntamento menziona la posta.

Questo ti lascia qualcosa come

class AppointmentScheduler {
    public function makeAppointmentFromJobApplication(JobApplication $application, \DateTime $time, Address $address = null) {
        $jobApplication->accept();

        return new Appointment($application->getCompany(),
            $application->getApplicant(),
            'Interview Invitation - '.$application->getCompany(),
            $time,
            $address ?: $application->getCompany()->getAddress()
        );
    }
}

La prossima domanda è, può un $jobApplication accettarsi davvero? Chi lo sta accettando? Il candidato? la compagnia?

Preferisco vedere l'azienda interagire esplicitamente con l'applicazione piuttosto che nasconderlo internamente. Questa è la vera interazione che sta accadendo qui.

Sembra che ci siano alcune interazioni più ricche che si nascondono nel tuo codice:

  • a Company che accetta un JobApplication
  • la Company che pianifica un Interview

e, come effetti collaterali da questo:

  • il Company che invia il Applicant il Invitation
  • il Application è accettato
risposta data 15.04.2014 - 08:50
fonte

Leggi altre domande sui tag