4Developers 2015: Jak (w końcu) zacząć pracować z DDD wykorzystując BDD - Kacper Gunia

Post on 15-Jul-2015

81 views 2 download

Transcript of 4Developers 2015: Jak (w końcu) zacząć pracować z DDD wykorzystując BDD - Kacper Gunia

How to (finally) start doing

DDD by using BDD

Kacper Gunia @cakper

So!ware Engineer @SensioLabsUK / @Inviqa

PHPers Silesia @PHPersPL

What is BDD?

Bug-drivenDevelopment ;)

Behaviour-driven development is about implementing an application

by describing its behaviour from the perspective of its

stakeholders.-- Dan North

BDD is about establishing a shared understanding of “done”

working from the outside in until you get there

-- Dan North

BDD shows you what to do nextaka Technical Discipline

How do we BDD?

Feature: Traveler searches for cheap itineraries In order to save money while travelling As a world traveler I want to search for the cheapest itinerary

Product owner writes scenarioand developer automates it

Developer writes scenarioand then automates it

No!

BDD is about communication!

flickr.com/photos/dvids/5638829762

Scenario: Successfully find cheapest direct flight Given the flight from "WAW" to "LHR" priced $30 was scheduled And the flight from "WAW" to "LHR" priced $50 was scheduled When I open the "/search" page And I fill "WAW" in the "Departure airport" field And I fill "LHR" in the "Destination airport" field And I click "Search" Then I should be redirected to "/results" page And I should see $30 in the "#cheapest-flight-price" block

Scenario: Successfully find cheapest direct flight Given the flight from "WAW" to "LHR" priced $30 was scheduled And the flight from "WAW" to "LHR" priced $50 was scheduled When I open the "/search" page And I fill "WAW" in the "Departure airport" field And I fill "LHR" in the "Destination airport" field And I click "Search" Then I should be redirected to "/results" page And I should see $30 in the "#cheapest-flight-price" block

Translation

Can we do better?

Mission accomplished BoysWe can go home now!

flickr.com/photos/dvids/5638829762

Translation again

How to fix that?

DDD

What is DDD about?

It’s about focusing on the domain and letting it affect the so"ware very

much-- Jimmy Nilsson

But WHY do we need it?

Everybody knows the jargonin their OWN FIELD

It's about common understanding

Ubiquitous language

Concrete examples are rooted in the problem domain

-- Matt Wynne

Domain Model

A domain model (...) is not just the knowledge in a domain expert’s head;

it is a rigorously organized and selective abstraction of that knowledge

-- Eric Evans

Model documentsthe knowledge

Pushing for ubiquitous language hard enough makes your examples a domain

model-- Konstantin Kudryashov

Scenario: Successfully find cheapest direct flight Given the flight from "WAW" to "LHR" priced $30 was scheduled And the flight from "WAW" to "LHR" priced $50 was scheduled When I open the "/search" page And I fill "WAW" in the "Departure airport" field And I fill "LHR" in the "Destination airport" field And I click "Search" Then I should be redirected to "/results" page And I should see $30 in the "#cheapest-flight-price" block

Scenario: Successfully find cheapest direct itinerary Given the search for the itinerary schedule And the itinerary from "WAW" to "LHR" priced $30 was planned in the schedule And the itinerary from "WAW" to "LHR" priced $50 was planned in the schedule When I search for cheapest itinerary from "WAW" to "LHR" Then the cheapest itinerary should cost $30

Modelling by example

Phase 1

Scenario: Successfully find cheapest direct itinerary Given the search for the itinerary schedule And the itinerary from "WAW" to "LHR" priced $30 was planned in the schedule And the itinerary from "WAW" to "LHR" priced $50 was planned in the schedule When I search for cheapest itinerary from "WAW" to "LHR" Then the cheapest itinerary should cost $30

Given the search for the itinerary schedule

/** * @Given /^the search for the itinerary schedule$/ */ public function theSearchForTheItinerarySchedule() { $this->itinerarySchedule = new ItinerarySchedule(); $this->search = new Search($this->itinerarySchedule); }

Design emerges

And the itinerary from "WAW" to "LHR" priced $30 was planned in the schedule

/** * @Given the itinerary from :fromAirport to :toAirport * priced $:price was planned in the schedule */ public function theItineraryFromToPricedWasPlannedInTheSchedule( $fromAirport, $toAirport, $price ) { $itinerary = new Itinerary( Airport::code($fromAirport), Airport::code($toAirport), Money::usd($price) );

$this->itinerarySchedule->plan($itinerary); }

When I search for cheapest itinerary from "WAW" to "LHR"

/** * @When I search for cheapest itinerary from :fromAirport to :toAirport */ public function iSearchForCheapestItineraryFromTo($fromAirport, $toAirport) { $this->cheapestItinerary = $this->search->forCheapest( Airport::code($fromAirport), Airport::code($toAirport) ); }

Then the cheapest itinerary should cost $30

/** * @Then the cheapest itinerary should cost $:price */ public function theCheapestItineraryShouldCost($price) { expect($this->cheapestItinerary->cost())->toBeLike(Money::usd($price)); }

Phase 2

@ui Scenario: Successfully find cheapest direct itinerary Given the search for the itinerary schedule And the itinerary from "WAW" to "LHR" priced $30 was planned in the schedule And the itinerary from "WAW" to "LHR" priced $50 was planned in the schedule When I search for cheapest itinerary from "WAW" to "LHR" Then the cheapest itinerary should cost $30

Given the search for the itinerary schedule

/** * @Given the search for the itinerary schedule */ public function theSearchForTheItinerarySchedule() { $this->visit("/search"); }

And the itinerary from "WAW" to "LHR" priced $30 was planned in the schedule

/** * @Given the itinerary from :fromAirport to :toAirport * priced $:price was planned in the schedule */ public function theItineraryFromToPricedWasPlannedInTheSchedule( $fromAirport, $toAirport, $price ) { $itinerary = new Itinerary( Airport::code($fromAirport), Airport::code($toAirport), Money::usd($price) );

$this->get("itinerary_schedule")->plan($itinerary); }

When I search for cheapest itinerary from "WAW" to "LHR"

/** * @When I search for cheapest itinerary from :fromAirport to :toAirport */ public function iSearchForCheapestItineraryFromTo($fromAirport, $toAirport) { $this->fillIn("#from-airport", $fromAirport); $this->fillIn("#to-airport", $toAirport);

$this->clickButton("Search"); }

Then the cheapest itinerary should cost $30

/** * @Then the cheapest itinerary should cost $:price */ public function theCheapestItineraryShouldCost($price) { $cheapestItinerary = $this->find("#cheapest-itinerary"); expect($cheapestItinerary)->toContainText(sprintf("From $%s", $price)); }

# behat.yml default: suites: domain: contexts: [ SearchContext ] ui: contexts: [ WebSearchContext ] filters: { tags: '@ui' }

Modelling by ExampleIn three (easy) steps

· Have the conversation· Model your objects

· Go again through UI*

*But

You (really) don't have to automate

everything!

But there is a problem

We ignored the depth of the domain

On purpose

You can't model the whole system using

one feature

Repeat the process and model the

planner

What if model has different requirements

in this context?

Bounded Context

Language is limited

Search Itinerary!=

Planner Itinerary!=

Booking Itinerary

Do not build fragile monoliths!

Build applications withBounded context in mind

--- Wrap up ---

Have the conversation

Do not separate the concepts from the implementation

You cannot build conceptual models

without considering implementation issues

Push forUbiquitous language

Use Behat to drive your ModelNot only the UI

"BDD is about conversations you have to

produce software"

"DDD is about how youexplore domain models

and how you articulate this"

Thanks!@cakper