I got confused about if a domain is required to access an external service in some state of it's life cycle.
The business rule is this :
If an Employee is accepting a JobSeeker's JobApplication, then the Employee must create an Appointment and have it e-mailed to the JobSeeker.
My current solutions are:
1 - Leave the mailing responsibility to the JobApplication domain
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;
}
}
I kinda feel awkward about this option, since it leaves the JobApplication entity to depends on the Mailing services.
2 - Create a new 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;
}
}
This option is nice since I only need to inject AppointmentService to the controller, but it leaves the Appointment class anemic.
I once read to never leave your domain to be dependent to domain services, the other way is fine. But some business rules do require services that existed out of the domain scope.
Is there another options?
Update - 1
A third option would be like this:
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());
}
}