kodas ir specifikacija: du zuikiai vienu šūviu
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.TRANSCRIPT
![Page 1: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/1.jpg)
Kodas ir specifikacija
du zuikiai vienu šūviu
code spec
Osvaldas GrigasVadim Platonov
![Page 2: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/2.jpg)
What happens next
● Motivation● Case study● Live demo
![Page 3: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/3.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/4.jpg)
Start with the code?
![Page 5: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/5.jpg)
Requirements?
![Page 6: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/6.jpg)
Specifications?
![Page 7: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/7.jpg)
Test Cases?
![Page 8: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/8.jpg)
code
Specification
Requirements
Test Cases
![Page 9: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/9.jpg)
What did we try?
![Page 10: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/10.jpg)
Behavior-Driven Development
![Page 11: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/11.jpg)
BDD?
![Page 12: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/12.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/13.jpg)
Let's Make a Payment.
![Page 14: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/14.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/15.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/16.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/17.jpg)
Room for improvement
![Page 18: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/18.jpg)
Developers
have to maintain stories, test code and their mapping
COOL
![Page 19: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/19.jpg)
What if...
specifications were generated from test code?
![Page 20: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/20.jpg)
Business language
● gets lost in implementation details
● is not enforced in code
COOL
![Page 21: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/21.jpg)
What if...
core parts of production code were reflected
in specifications?
![Page 22: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/22.jpg)
Specifyscenarios
in plain text
Automatescenariosas tests
Specifyscenarios
in code
![Page 23: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/23.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/24.jpg)
code
Yes I can! *
![Page 25: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/25.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/26.jpg)
DDD? CQRS?Event
Sourcing?
![Page 27: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/27.jpg)
Domain-Driven DesignCrash Course
![Page 28: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/28.jpg)
DDD
Ubiquitous Language
Account
Debit
CreditAsset
Liability
Balance
![Page 29: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/29.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/30.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/31.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/32.jpg)
DDD
Isolated Domain
Domain
data storemessage
broker
client services
![Page 33: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/33.jpg)
Event Sourcing Crash Course
![Page 34: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/34.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/35.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/36.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/37.jpg)
Command-QueryResponsibilitySegregation
Crash
Course
![Page 38: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/38.jpg)
Read Model
CQRS
Write Model
QueryCommand
![Page 39: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/39.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/40.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/41.jpg)
Putting it all together
![Page 42: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/42.jpg)
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](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/43.jpg)
Domain (Aggregates)
Command Handlers
User Story Tests
Events
Commands
MockEvent Store
Events
Events
Testingthe Domain
![Page 44: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/44.jpg)
Live DemoCoding Specifications
![Page 45: Kodas ir specifikacija: du zuikiai vienu šūviu](https://reader030.vdocuments.pub/reader030/viewer/2022020217/5481d3a1b07959570c8b460f/html5/thumbnails/45.jpg)
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/