Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cocoders Flow – czyli jak staramy się modelować...

Cocoders Flow – czyli jak staramy się modelować i tworzyć aplikacje webowe.

Chcemy powiedzieć o naszym sposobie na modelowanie i projektowanie aplikacji internetowych.
W gruncie rzeczy ten sposób jest w dość znacznym stopniu związany z BDD (Behavior Driven Development) oraz z niektórymi aspektami DDD.
Opowiemy kilka słów o tym jak dochodzimy do wymagań, jak planujemy domenę biznesową na podstawie tych wymagań, w jaki sposób integrujemy tą domenę.
Pokażemy w jaki sposób możemy taki kod zintegrować z frameworkiem Symfony2 (i nie tylko ;))

Leszek Prabucki

April 16, 2015
Tweet

More Decks by Leszek Prabucki

Other Decks in Programming

Transcript

  1. As a receptionist in the clinic, I would like arrange

    a visit without worrying about whether patient is insured so I should be able check such information in the system instead of calling insurance company
  2. Przykład 1 – pacjent bez ubezpieczenia Scenario: Checking information for

    patient without insurance Given I am receptionist in the clinic And patient does not have medical insurance When I am find and open that patient case Then I should see that the patient can not be scheduled for a medical visit because he is not insured
  3. Przykład 2 – ubezpieczony pacjent Scenario: Checking information for patient

    with insurance Given I am receptionist in the clinic And patient has medical insurance When I am find and open that patient case Then I should see that the patient can be scheduled for a medical visit
  4. Kandydaci na nasze obiekty domeny Scenario: Checking information for patient

    without insurance Given I am receptionist in the clinic And patient does not have medical insurance When I am find and open that patient case Then I should see that the patient can not be scheduled for a medical visit because he is not insured
  5. BEHAT class FeatureContext implements Context { /** * @Given I

    am receptionist in the clinic */ public function iAmReceptionistInTheClinic() { //tutaj jakiś mój kod } }
  6. Kandydaci na nasze obiekty domeny – Placówka Scenario: Checking information

    for patient without insurance Given I am receptionist in the clinic And patient does not have medical insurance When I am find and open that patient case Then I should see that the patient can not be scheduled for a medical visit because he is not insured
  7. Modelowanie domeny /** * @Given I am receptionist in the

    clinic */ public function iAmReceptionistInTheClinic() { $this->clinic->hireReceptionist( $firstName = 'Jan', $lastName = 'Kowalski', $idNumber = '80081012345' ); }
  8. Modelowanie domeny /** * @Given I am receptionist in the

    clinic */ public function iAmReceptionistInTheClinic() { $this->clinic->hireEmployee( new Receptionist( $firstName = 'Jan', $lastName = 'Kowalski', $idNumber = '80081012345' ) ); }
  9. Modelowanie domeny Co jest nam potrzebne do utworzenia obiektu “Clinic”?

    Jakie dane są nam potrzebne i po co są nam potrzebne?
  10. Modelowanie domeny $this->clinic = new Clinic( 'Clinic name', new Address(

    $postalCode = '80-283', $city = 'Gdańsk', $street = 'Królewskie Wzgórze' ), new TaxIdentificationNumber('123-456-32-18'), new NationalEconomyRegisterNumber('123456785') ) ;
  11. Hermetyzacja modelu – anemic model $this->clinic = new Clinic(); $this->clinic->setPostalCode('80-283');

    $this->clinic->setCity('Gdańsk'); $this->clinic->setStreet('Królewskie Wzgórze'); $this->clinic->setTaxIdNumber('123-456-32-18'); $this ->clinic ->setNationalEconomyRegisterNumber('123456785') ;
  12. Modelowanie aplikacji – od modelu do aplikacji $this->clinic = new

    Clinic( 'Clinic name', new Address( $postalCode = '80-283', $city = 'Gdańsk', $street = 'Królewskie Wzgórze' ), new TaxIdentificationNumber('123-456-32-18'), new NationalEconomyRegisterNumber('123456785') ) ; //tutaj musimy zapisać placówke w bazie
  13. Modelowanie aplikacji – abstrakcja oraz porty namespace Cocoders\MedicalClinic; interface ClinicRegistry

    { public function add(Clinic $clinic); public function find( Clinic\TaxIdentificationNumber $taxIdentificationNumber ); }
  14. Modelowanie aplikacji – od modelu do aplikacji $clinicRegistry = new

    InMemory\ClinicRegistry() $this->clinic = new Clinic( 'Clinic name', new Address( $postalCode = '80-283', $city = 'Gdańsk', $street = 'Królewskie Wzgórze' ), new TaxIdentificationNumber('123-456-32-18'), new NationalEconomyRegisterNumber('123456785') ) ; $clinicRegistry->add($this->clinic);
  15. Modelowanie aplikacji – przykładowy przypadek użycia use Cocoders\MedicalClinic\ClinicRegistry; class SettingUpClinic

    { /** * @var ClinicRegistry */ private $registry; public function __construct(ClinicRegistry $registry) { $this->registry = $registry; } public function execute(SettingUpClinic\Command $command) }
  16. class SettingUpClinic { public function execute(SettingUpClinic\Command $command) { $services =

    array_map(function ($serviceName) { return new Clinic\Service($serviceName); }, $command->services); $clinic = new Clinic( $command->name, new Clinic\Address( $command->postalCode, $command->city, $command->street ), $services, new Clinic\TaxIdentificationNumber($command->taxIdNumber), new Clinic\NationalEconomyRegisterNumber( $command->nationalRegistryNumber ) ); $this->registry->add($clinic); }
  17. Modelowanie aplikacji – od modelu do aplikacji $clinicRegistry = new

    InMemory\ClinicRegistry(); $settingUpClinic = new SettingUpClinic($clinicRegistry); $settingUpClinic->execute(new SettingUpClinic\Command( $name = 'Clinic name', $postalCode = '80-283', $city = 'Gdańsk', $street = 'Królewskie Wzgórze', $services = [ 'MRI', 'CT' ], $taxIdNumber = '123-456-32-18', $nationalRegistryNumber= '123456785' ));
  18. Modelowanie aplikacji – następny przypadek użycia class FeatureContext implements Context

    { public function __construct() { // settingUpClinic use case $this->hireReceptionist = new HireReceptionist( $this->clinicRegistry ); } }
  19. Modelowanie aplikacji – następny przypadek użycia /** * @Given I

    am receptionist in the clinic */ public function iAmReceptionistInTheClinic() { $this->hireReceptionist->execute( new HireReceptionist\Command( $taxIdNumber = '123-456-32-18', $firstName = 'Jan', $lastName = 'Kowalski', $idNumber = '80081012345' ) ); }
  20. Integracja z frameworkiem – Doctrine w Symfony doctrine: dbal: #...

    orm: entity_managers: default: auto_mapping: true mappings: MedicalClinic: type: yml dir: %kernel.root_dir %/../src/Cocoders/Adapters/Doctrine/ORM/mapping/MedicalCli nic prefix: Cocoders\MedicalClinic is_bundle: false
  21. use Cocoders\MedicalClinic\ClinicRegistry as ClinicRegistryInterface; use Doctrine\Common\Persistence\ObjectManager; class ClinicRegistry implements ClinicRegistryInterface

    { private $manager; public function __construct(ObjectManager $manager) { $this->manager = $manager; } public function add(Clinic $clinic) { $this->manager->persist($clinic); $this->manager->flush($clinic); } public function find(Clinic\TaxIdentificationNumber $taxIdentificationNumber) { return $this ->manager ->getRepository(Clinic::class) ->findOneBy(['taxNumber' => (string) $taxIdentificationNumber]) ; }
  22. Integracja z frameworkiem – Symfony DIC services: cocoders.medical_clinic.clinic_registry: class: Cocoders\Adapters\Doctrine\ClinicRegistry

    arguments: ["@doctrine.orm.default_entity_manager"] cocoders.medical_clinic.use_case.setting_up_clinic: class: Cocoders\MedicalClinic\UseCase\SettingUpClinic arguments: ["@cocoders.medical_clinic.clinic_registry"] cocoders.medical_clinic.use_case.hire_receptionist: class: Cocoders\MedicalClinic\UseCase\HireReceptionist arguments: ["@cocoders.medical_clinic.clinic_registry"]
  23. Integracja z frameworkiem – Kontroler /** * @Route("/clinic/{taxNumber}/receptionist") * @Method({"GET",

    "POST"}) */ public function hireReceptionistAction(Clinic $clinic, Request $request) { $form = $this->createForm(new HireReceptionistFormType()); $form->handleRequest($request); if ($form->isValid()) { $this ->get('cocoders.medical_clinic.use_case.hire_receptionist') ->execute(new HireReceptionist\Command( (string) $clinic->getTaxIdNumber(), $form->get('firstName')->getData(), $form->get('lastName')->getData(), $form->get('idNumber')->getData() )); //return $this->redirectToRoute() } //return $this->render() }