45
Kodas ir specifikacija du zuikiai vienu šūviu code spec Osvaldas Grigas Vadim Platonov

Kodas ir specifikacija: du zuikiai vienu šūviu

Embed Size (px)

DESCRIPTION

Osvaldo Grigo ir Vadim Platonov pranešimas "Kodas ir specifikacija: du zuikiai vienu šūviu" skaitytas Agile dienoje 2013 gegužės 9 d. Kaip Behavior-Driven Development idėjas pritaikėme kurdami finansinių paslaugų valdymo sistemą. Kaip galima kodą paversti pirminiu tiesos šaltiniu, kai automatizuotais testais užrašomi reikalavimai. Trumpai supažindinsime su mūsų architektūros specifika, paremta DDD, CQRS ir Event Sourcing principais, kurie atveria naujas galimybes, bet kartu atneša naujų iššūkių. Kokias darėme klaidas, ir kaip jų išvengti.

Citation preview

Page 1: Kodas ir specifikacija: du zuikiai vienu šūviu

Kodas ir specifikacija

du zuikiai vienu šūviu

code spec

Osvaldas GrigasVadim Platonov

Page 2: Kodas ir specifikacija: du zuikiai vienu šūviu

What happens next

● Motivation● Case study● Live demo

Page 3: Kodas ir specifikacija: du zuikiai vienu šūviu

Specification?

Requirements?

Scenarios?

Documentatio

n?

User stories?

What do we need?

Test cases?

Use cases?

Code?

Examples?Acceptance criteria?

Page 4: Kodas ir specifikacija: du zuikiai vienu šūviu

Start with the code?

Page 5: Kodas ir specifikacija: du zuikiai vienu šūviu

Requirements?

Page 6: Kodas ir specifikacija: du zuikiai vienu šūviu

Specifications?

Page 7: Kodas ir specifikacija: du zuikiai vienu šūviu

Test Cases?

Page 8: Kodas ir specifikacija: du zuikiai vienu šūviu

code

Specification

Requirements

Test Cases

Page 9: Kodas ir specifikacija: du zuikiai vienu šūviu

What did we try?

Page 10: Kodas ir specifikacija: du zuikiai vienu šūviu

Behavior-Driven Development

Page 11: Kodas ir specifikacija: du zuikiai vienu šūviu

BDD?

Page 12: Kodas ir specifikacija: du zuikiai vienu šūviu

Domain experts + DevsSpecify scenarios in plain text

DevsAutomate scenarios as tests

JenkinsPublish scenarios to Wiki

Domain expertsDescribe features

Page 13: Kodas ir specifikacija: du zuikiai vienu šūviu

Let's Make a Payment.

Page 14: Kodas ir specifikacija: du zuikiai vienu šūviu

Domain expertsDescribe a feature

Account can only be debited if it has sufficient balance and is not blocked for debit.

Page 15: Kodas ir specifikacija: du zuikiai vienu šūviu

Domain experts + DevsSpecify scenarios

Scenario: Debiting an account with sufficient balance

Given a liability account LT000001 with a balance of 100 USD,

not blocked for debit

When the account LT000001 is debited for 20 USD

Then the debit succeeds producing a running balance of 80 USD

Page 16: Kodas ir specifikacija: du zuikiai vienu šūviu

DevsAutomated scenarios as tests

public class DebitAccount extends JUnitStory {

@Given("a $type account $number with a balance of $balance USD, $debitStatus for debit") public void givenAnAccount(String type, String number, int balance, String debitStatus) { // ... }

@When("the account $number is debited for $amount USD") public void whenAccountIsDebitedBy(String number, int amount) { // ... }

@Then("the debit $result producing a running balance of $balance USD") public void thenDebitResultsIn(String result, int balance) { // ... }}

Page 17: Kodas ir specifikacija: du zuikiai vienu šūviu

Room for improvement

Page 18: Kodas ir specifikacija: du zuikiai vienu šūviu

Developers

have to maintain stories, test code and their mapping

COOL

Page 19: Kodas ir specifikacija: du zuikiai vienu šūviu

What if...

specifications were generated from test code?

Page 20: Kodas ir specifikacija: du zuikiai vienu šūviu

Business language

● gets lost in implementation details

● is not enforced in code

COOL

Page 21: Kodas ir specifikacija: du zuikiai vienu šūviu

What if...

core parts of production code were reflected

in specifications?

Page 22: Kodas ir specifikacija: du zuikiai vienu šūviu

Specifyscenarios

in plain text

Automatescenariosas tests

Specifyscenarios

in code

Page 23: Kodas ir specifikacija: du zuikiai vienu šūviu

Story: Debiting an account

Scenario: Sufficient balance

Given: Account with

● id: LT000001● balance: USD 100● balance type: Liability● additional currency support: Disallowed● debiting allowed: Yes● crediting allowed: Yes

When: Debit account with

● account number: LT000001● amount: 20● currency: USD

Then: Account debited

● with amount 20● with running balance 80● with currency "USD"

code

Can I do that?

Page 24: Kodas ir specifikacija: du zuikiai vienu šūviu

code

Yes I can! *

Page 25: Kodas ir specifikacija: du zuikiai vienu šūviu

public class Debiting_an_account extends Story<Account, DebitAccount> {

@Test public void sufficient_balance() { givenEvents( new AccountCreated("LT000001", "USD", LIABILITY), accountCredited("LT000001", "any", $(30, "USD")), accountCredited("LT000001", "any", $(70, "USD")));

onCommand(new DebitAccount("LT000001", "any", $(20), "USD"));

expectEvent(AccountDebited.class) .with("amount", $(20)) .with("runningBalance", $(80)) .with("currency", "USD"); }

}

Page 26: Kodas ir specifikacija: du zuikiai vienu šūviu

DDD? CQRS?Event

Sourcing?

Page 27: Kodas ir specifikacija: du zuikiai vienu šūviu

Domain-Driven DesignCrash Course

Page 28: Kodas ir specifikacija: du zuikiai vienu šūviu

DDD

Ubiquitous Language

Account

Debit

CreditAsset

Liability

Balance

Page 29: Kodas ir specifikacija: du zuikiai vienu šūviu

public class Debiting_an_account extends Story<Account, DebitAccount> {

@Test public void sufficient_balance() { givenEvents( new AccountCreated("LT000001", "USD", LIABILITY), accountCredited("LT000001", "any", $(30, "USD")), accountCredited("LT000001", "any", $(70, "USD")));

onCommand(new DebitAccount("LT000001", "any", $(20), "USD"));

expectEvent(AccountDebited.class) .with("amount", $(20)) .with("runningBalance", $(80)) .with("currency", "USD"); }

}

Page 30: Kodas ir specifikacija: du zuikiai vienu šūviu

DDD

Entity

public class Account extends AggregateRoot {

private Balance balance;

public void credit(Amount amount) { ... }

public void debit(Amount amount) { ... }

}

Page 31: Kodas ir specifikacija: du zuikiai vienu šūviu

public class Debiting_an_account extends Story<Account, DebitAccount> {

@Test public void sufficient_balance() { givenEvents( new AccountCreated("LT000001", "USD", LIABILITY), accountCredited("LT000001", "any", $(30, "USD")), accountCredited("LT000001", "any", $(70, "USD")));

onCommand(new DebitAccount("LT000001", "any", $(20), "USD"));

expectEvent(AccountDebited.class) .with("amount", $(20)) .with("runningBalance", $(80)) .with("currency", "USD"); }

}

Page 32: Kodas ir specifikacija: du zuikiai vienu šūviu

DDD

Isolated Domain

Domain

data storemessage

broker

client services

Page 33: Kodas ir specifikacija: du zuikiai vienu šūviu

Event Sourcing Crash Course

Page 34: Kodas ir specifikacija: du zuikiai vienu šūviu

Events

Capture all changes to domain state

public class AccountDebited {

public String accountNumber;

public BigDecimal amount;

public BigDecimal runningBalance;

public String currency;

}

Page 35: Kodas ir specifikacija: du zuikiai vienu šūviu

Events

Never lose data

AccountCreated 0

AccountCredited 300

AccountCredited 1000

AccountCredited 1500

AccountDebited 900

AccountCredited 1200

AccountDebited 800

AccountDebited 700

time

Balance

Page 36: Kodas ir specifikacija: du zuikiai vienu šūviu

public class Debiting_an_account extends Story<Account, DebitAccount> {

@Test public void sufficient_balance() { givenEvents( new AccountCreated("LT000001", "USD", LIABILITY), accountCredited("LT000001", "any", $(30, "USD")), accountCredited("LT000001", "any", $(70, "USD")));

onCommand(new DebitAccount("LT000001", "any", $(20), "USD"));

expectEvent(AccountDebited.class) .with("amount", $(20)) .with("runningBalance", $(80)) .with("currency", "USD"); }

}

Page 37: Kodas ir specifikacija: du zuikiai vienu šūviu

Command-QueryResponsibilitySegregation

Crash

Course

Page 38: Kodas ir specifikacija: du zuikiai vienu šūviu

Read Model

CQRS

Write Model

QueryCommand

Page 39: Kodas ir specifikacija: du zuikiai vienu šūviu

CQRS

Command

public class DebitAccount {

public final String accountNumber;

public final BigDecimal amount;

public final String currency;

}

Page 40: Kodas ir specifikacija: du zuikiai vienu šūviu

public class Debiting_an_account extends Story<Account, DebitAccount> {

@Test public void sufficient_balance() { givenEvents( new AccountCreated("LT000001", "USD", LIABILITY), accountCredited("LT000001", "any", $(30, "USD")), accountCredited("LT000001", "any", $(70, "USD")));

onCommand(new DebitAccount("LT000001", "any", $(20), "USD"));

expectEvent(AccountDebited.class) .with("amount", $(20)) .with("runningBalance", $(80)) .with("currency", "USD"); }

}

Page 41: Kodas ir specifikacija: du zuikiai vienu šūviu

Putting it all together

Page 42: Kodas ir specifikacija: du zuikiai vienu šūviu

MongoDBDomain

(Aggregates)

Projectors (Groovy)

Command Handlers

Query Services

Client (Wicket)

Event StoreEvents

Events

Commands Projections

Projections

Events

Page 43: Kodas ir specifikacija: du zuikiai vienu šūviu

Domain (Aggregates)

Command Handlers

User Story Tests

Events

Commands

MockEvent Store

Events

Events

Testingthe Domain

Page 44: Kodas ir specifikacija: du zuikiai vienu šūviu

Live DemoCoding Specifications

Page 45: Kodas ir specifikacija: du zuikiai vienu šūviu

Attributions

Photo: Accidents - after crash 1978 SkodaAuthor: Istvan TakacsURL: http://commons.wikimedia.org/wiki/File:Accidents_-_after_crash_1978_Skoda.jpgLicense: http://creativecommons.org/licenses/by-sa/3.0/deed.en

Photo: Tram in Kraków, PolandAuthor: AstrorekURL: http://commons.wikimedia.org/wiki/File:Tramwaj_krakow_3.jpgLicense: http://creativecommons.org/licenses/by-sa/3.0/deed.en

Photo: Mokate 3in1© Copyright 2013 Mokate S.A.URL: http://www.mokate.eu/

Image: JBehave examples@ Copyright JBehaveURL: http://jbehave.org/

Image: Rainbow@ Copyright MoonglowlillyURL: http://moonglowlilly.deviantart.com/art/PNG-RAINBOW-324144428

Image: Debit cardURL: http://www.capitalistbanter.com/2011/05/featured/the-best-and-worst-time-to-use-a-debit-card/