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

Turn your startup in a stayup with DDD

Turn your startup in a stayup with DDD

A real story about DDD, startups, agility, TDD, debt (both technical and financial), culture, failure and success.

Marijn Huizendveld

December 01, 2015
Tweet

More Decks by Marijn Huizendveld

Other Decks in Programming

Transcript

  1. – Pink Tie V.O.F. “The domain model will represent available

    Admissions to Events that are organized by Promoters. (…)”
  2. – Pink Tie V.O.F. “(…) Once an Admission has been

    acquired a Ticket will be issued, granting the TicketHolder access to the Event for the acquired Admission.”
  3. final class GeneralTermsAndConditionsAgreementAccepted extends DomainEvent { private $agreementId; private $customerRegistrationId;

    private $signee; private $organization; ! public function __construct( Uuid $agreementId, $playhead, Uuid $customerRegistrationId, $signee, $organization ) { parent::__construct($playhead); ! $this->agreementId = $agreementId; $this->customerRegistrationId = $customerRegistrationId; $this->signee = $signee; $this->organization = $organization; } }
  4. public function handle(StartPurchaseOrderIdealPayment $command) { $purchaseOrder = $this->purchaseOrders->find($command->getPurchaseOrderId()); $idealMerchant =

    $this->idealMerchants->find($command->getIdealMerchantId()); $idealIssuer = $this->idealIssuers->find($command->getIdealIssuerId()); ! $idealTransaction = $purchaseOrder->startIdealTransaction( $command->getIdealTransactionId(), $idealMerchant, $this->idealAcquirerConnection, $idealIssuer, $command->getReturnUrl(), $command->getRandomCode(), $command->getIdealTransactionFee() ); ! $purchaseOrder->startPaymentProcess($idealTransaction); ! // persistence order is important $this->purchaseOrders->add($purchaseOrder); $this->idealTransactions->add($idealTransaction); }
  5. – Pink Tie V.O.F. “The domain model will represent available

    Admissions to Events that are organized by Promoters. (…)”
  6. – Pink Tie V.O.F. “(…) The domain model will represent

    available Admissions to Events that are organized by Promoters. These Admissions can be acquired given a PricingPolicy. (…)”
  7. – Pink Tie V.O.F. “(…) Once a Admission has been

    required a Ticket will be issued, granting the TicketHolder access to the Event for the acquired Admission.”
  8. DDD in startups Cost Specific knowledge Difficult to hire Difficult

    to train Code ceremony Skilled developers Tests can slow down Knowledge crunching Initial slowdown Access to experts Perfection Slow pace of change Stiffness Lack of feedback DDD fixation Wrong abstractions Cultural issues Traps DDD all the things Lack of context Over design Unfocused effort No refinement Missing abstractions Mismatch with ubiquitous language Mismatch between model and implementation Technical debt in domain model Inertia Expensive to pay back Knowledge leaks Domain model becomes anemic Difficult to refactor High risk of issues Coupling to infrastructure Difficult to move to other infrastructure Reduces testability Incorrect boundaries Aggregates Bounded contexts Modules No domain experts Lack of feedback Self fulfilling prophecy Slow Incorrect intent Cripples communication False suggestions Leads away from real problems Frameworkization Wrong level of abstractions Wrong audience Coupling False vision/wrong perspective Leads away from the real problems Difficult to identify Even more difficult to correct Waterfall Slow time to market No feedback cycle Change of course is tough Wide vision Everything is core No time to become excellent Unskilled team Difficult to solve in the short term Expensive in the long term Many new techniques at once Lack of focus on domain Lack of refinent High risk low reward Knowledge silos Team members Areas of the code Integrations under the radar Benefit Ubiquitous language Understanding of the domain Intent Commands Analytics Evolving Testing Support Debugging Domain events Extensibility Scaling Evolving Testing Analytics Support Debugging Abstractions Emergent design Opportunities Pivot Maintainable Simplicity Agility Ease of use Adoption Little support Eases communications Domain experts Simplifies marketing effort Team members Behavior driven tests Agility Flat learning curve Visualization Bounded context Scope Isolation Low risk Decoupled Focus Specialization Fun & excitement Tools Process Knowledge crunching Explore boundaries Experiment Narratives Diagrams Visualization techniques Going backwards Visual tests Ubiquitous language Read vs. write UX state charts Boxes & arrows Estimating ROI Cost/benefit Pomodorros Planning Event storming Modeling whirlpool Agile Infrastructure Clouds Messaging CI Monitoring Code TDD Tactical patterns Natural language Ubiquitous language Temporal evolution Modularity API design Existing patterns Event sourcing CQRS Hacks Follow the current
  9. /** * @test * @dataProvider ProvidePreSaleIdEventDetailsAndAnotherEventName::that_are_valid */ public function corrects_name_of_event(

    Uuid $aPreSaleId, EventDetails $someEventDetails, $anotherEventName ) { $this->scenarioToTest() ! ->given( GeneralAdmissionPrepared::withPreSaleId($aPreSaleId) ->withEventDetails($someEventDetails) ->withCapacity(1) ) ! ->when(CorrectNameOfEvent::v1($aPreSaleId, $anotherEventName)) ! ->then(NameOfEventWasCorrected::v1($aPreSaleId, 1, $anotherEventName)); }
  10. Feature: Register account In order to become a customer As

    a promoter I want to register for an account. ! Scenario: Successful registration ! Scenario: Customer is already registered ! Scenario: Forgot to accept the General Terms and Conditions ! Scenario: Forgot to enter name ! Scenario: Forgot to enter password ! Scenario: Forgot to enter organization
  11. final class PleaseReserveTickets extends CommandOrderProcess { ! private $orderProcess, $reservationNumber,

    $tickets, $channel; ! public function __construct ( $orderProcess, $reservationNumber, $tickets, $channel ) { $this->orderProcess = (string) $orderProcess; $this->reservationNumber = (string) $reservationNumber; $this->tickets = (string) $tickets; $this->channel = (string) $channel; } }
  12. final class SorryInvalidOrderPolicy extends DomainException { ! public function __construct

    ($aMessage) { parent::__construct($aMessage); } ! static function becauseMinOrderQuantityExceedsMaxOrderQuantity () { return new SorryInvalidOrderPolicy( 'Sorry, the minimum order quantity should be less than or ' 'equal to the maximum order quantity.' ); } }
  13. final class ProvideOrderNumberInput extends PHPUnit_Framework_DataProvider { ! static public function

    that_is_semantically_correct () { return [ 'First order number for firsthand tickets' => [ 'aNumber' => 'A000001' ] ]; } }