SMS and Voice services
MMO Games
local positioning systems
TV productions
Healthcare
e-commerce
websites
SEO
online video
Help all the teams involved in the production do their job quicker, easier and with better quality...
We didn't really know what we were supposed to create
"Your architectures should tell readers about the system, not about the frameworks you used in your system."
"Uncle" Bob Martin
/**
* @Route("/contestants/notes/new", methods={"POST"}, name="contestant_note_new")
* @IsGranted("IS_CASTING_TEAM")
*
*/
public function noteNew(Request $request, Contestant $contestant, EventDispatcherInterface $eventDispatcher): Response
{
$note = new Note();
$note->setAuthor($this->getUser());
$contestant->addNote($note);
$form = $this->createForm(NoteType::class, $note);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($note);
$em->flush();
$event = new GenericEvent($note);
$eventDispatcher->dispatch(Events::CONTESTANT_NOTE_CREATED, $event);
return $this->redirectToRoute('contestang', ['id' => $contestant->getId()]);
}
return $this->render('contestant/contestant_form_error.html.twig', [
'contestant' => $contestant,
'form' => $form->createView(),
]);
}
Authorization
Validation
Business logic
namespace \MusicIdol\Domain\Contestants;
class NewNote {
private $em;
private $ed;
public function __construct(EntityManagerInterface $em, EventDispatcherInterface $ed) {
$this->em = $em;
$this->ed = $ed;
}
public function __invoke(User $user, int $contestantId, string $noteText) {
if (empty ($noteText)) {
throw new InvalidArgumentException('The note can not be empty');
}
$contestant = $this->em->find(Contestant::class, $contestantId);
if (!$contestant) {
throw new EntityNotFoundException('Contestant does not exist');
}
if (!$user->isGranted('IS_CASTING_TEAM')) {
throw new UnauthorizedException('User is not authorized to add notes');
}
$note = new Note();
$note->setText($noteText);
$note->setAuthor($user);
$contestant->addNote($note);
$this->em->persist($note);
$this->em->flush();
$event = new GenericEvent($note);
$this->ed->dispatch(Events::CONTESTANT_NOTE_CREATED, $event);
return true;
}
}
/**
* @Route("/contestants/{id}/notes/new", methods={"POST"}, name="contestant_note_new")
*
*/
public function noteNew(Request $request, Contestant $contestant, EntityManagerInterface $em,
EventDispatcherInterface $ed): Response
{
$appService = new NewNote($em, $ed);
try {
if ($appService($this->getUser(), $contestant, $request->request->get('note'))) {
return $this->redirectToRoute('contestang', ['id' => $contestant->getId()]);
}
} catch (...) {
/* handle error here */
}
return $this->render('contestant/contestant_form_error.html.twig', [
'contestant' => $contestant,
'form' => $form->createView(),
]);
}
[
'match' => [
'method' => 'GET',
'path' => '/contestants/{id}/notes/new'
],
'service' => [
'className' => MusicIdol\ContestantsManagement\AddNote::class,
'parametersMapping' => [
'contestantId' => 'parameters.id',
'textNote' => 'post.note'
],
'responder' => \UI\JsonResponder::class
]
],
...
[
'match' => [
'command' => 'add-note'
],
'service' => [
'className' => MusicIdol\ContestantsManagement\AddNote::class,
'parametersMapping' => [
'contestantId' => '--contestant-id',
'textNote' => '--note'
],
'responder' => \UI\ConsoleResponder::class
]
],
...
Domain Driven Design: Tackling Complexity in the Heart of Software
Eric Evans
Clean Architecture Talk
https://www.youtube.com/watch?v=Nsjsiz2A9mg
"Uncle" Bob Martin
Action Domain Responder
https://github.com/pmjones/adr
Paul Jones
Milko Kosturkov
@mkosturkov
linkedin.com/in/milko-kosturkov
mailto: mkosturkov@gmail.com
These slides:
https://slides.com/milkokosturkov/minimize-framework