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

最新DDDアーキテクチャとAkkaでの実装ヒントについて

 最新DDDアーキテクチャとAkkaでの実装ヒントについて

DDD+CQRS+Event SourcingとAkkaでの実装ヒントを解説。

かとじゅん

March 26, 2016
Tweet

More Decks by かとじゅん

Other Decks in Programming

Transcript

  1. ࠷৽DDDΞʔΩςΫνϟ
    ͱAkkaͰͷ࣮૷ώϯτ
    ʹ͍ͭͯ
    Ճ౻५Ұ ͔ͱ͡ΎΜ

    $IBU8PSL

    View full-size slide

  2. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/3/26 © ChatWork All rights reserved.
    ࣗݾ঺հ
    w!KJLP
    w$IBU8PSL͔Βདྷ·ͨ͠ɻ
    wࡽ͍ͯ͠Δίʔυ
    wIUUQTHJUIVCDPNKJLP
    wIUUQTHJUIVCDPNTJTJPI
    wIUUQTHJUIVCDPNDIBUXPSLTCUBXT
    wIUUQTHJUIVCDPNDIBUXPSLTCUEPDLFS
    w࠷ۙ͸$234&4 "LLBͳͲʹ஫໨͍ͯ͠·͢ɻ
    w%%%Ͱ໎ࢠʹͳͬͯͨΒ੠͔͚͍ͯͩ͘͞ɻΞυό
    Πε͠·͢ɻ

    View full-size slide

  3. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/3/26 © ChatWork All rights reserved.
    ΞδΣϯμ
    w4DBMBͰͷ࠷৽%%%ΞʔΩςΫνϟͱ"LLBͰͷ࣮૷
    ώϯτʹ͍ͭͯ࿩͠·͢ɻ
    wΞδΣϯμ
    w%%%$234&WFOU4PVSDJOHʹ͍ͭͯ
    w%%%ͱ͸Կ͔ʁ
    w$234ͱ͸Կ͔ʁ
    w&WFOU4PVSDJOHͱ͸Կ͔ʁ
    w"LLBͰͲͷΑ͏ʹ࣮૷͢Δͷ͔ʁ

    View full-size slide

  4. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 4
    %%%$234&WFOU4PVSDJOHʹ͍ͭͯ

    View full-size slide

  5. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 5
    ':*%%%ؔ࿈͓͢͢Ίਤॻ
    w %%%
    w ʹൃץɻ
    w ೔ຊޠ൛
    w %&""
    w ʹൃץɻ
    w ೔ຊޠ൛ɻ
    w *%%%
    w ʹൃץɻ
    w ೔ຊޠ൛ɻ
    w 3.1
    w ʹൃץɻ
    w ಡॻձ͋Γ·͢

    View full-size slide

  6. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 6
    %%%ͱ͸Կ͔ʁ
    w Ϗδωεͷ஌ࣝΛ൓өͨ͠ιϑτ΢ΣΞϞσϧ
    ϢϏΩλεݴޠ
    ڥք͚ͮΒΕͨ
    ίϯςΩετ
    αϒυϝΠϯ
    ϨΠϠʔԽ
    ΞʔΩςΫνϟ
    ϏϧσΟϯά
    ϒϩοΫ
    υϝΠϯϞσϧ
    ϥΠϑαΠΫϧ؅ཧ
    ઓུతϞσϦϯά ઓज़తϞσϦϯά
    ରԠ෇͘
    υϝΠϯͷִ཭
    •υϝΠϯର͢ΔιϦϡʔγϣϯ
    •υϝΠϯϞσϧͷ෼཭/౷߹ઓུ
    ໰୊ͱͦͷ༏ઌॱҐͷఆٛ
    ઐ໳Ոͱ։ൃऀͷڞ௨ݴޠ

    View full-size slide

  7. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 7
    ϨΠϠʔԽΞʔΩςΫνϟ
    w υϝΠϯΛִ཭͢Δ͜ͱ͕໨తɻ
    Client
    Application
    Database
    Domain Layer
    Application Layer
    Records
    Infrastructure Layer
    Form Dto
    Aggregate
    Record Record
    Aggregate

    View full-size slide

  8. case class User(identifier: UserId, statusType: StatusType.Value,
    name: UserName, profile: UserProfile, config: UserConfig)
    case class UserProfile(name: String, address: ContactAddress)
    case class UserConfig(hashedPassword: String)
    ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 8
    ϨΠϠʔԽΞʔΩςΫνϟͷ࣮૷্ͷ໰୊
    wϨΠϠʔΛލ͙Ϟσϧม׵
    wॻ͖ࠐΈ3FRVFTU+40/ˠू໿ˠ3FDPSE
    wಡΈࠐΈ3FTQPOTF40/ˡू໿ˡ3FDPSE
    wू໿ͱςʔϒϧϞσϧͷΠϯϐʔμϯεϛεϚον
    id status name zip_code pref_code city_name address … hashed_password
    1 1 xxx 123-4567 yyy zzz aaa … xyeogkdeid
    … … … … … … … … …
    n … … … … … … … …
    ΠϯϐʔμϯεϛεϚον

    View full-size slide

  9. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 9
    ଞͷ%%%ΞʔΩςΫνϟྫ

    View full-size slide

  10. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 10
    ϔΩαΰφϧcΫϦʔϯcΦχΦϯ
    ΞʔΩςΫνϟ
    w ґଘؔ܎ͷٯసͱ֎෦ɾ಺෦ͷ֓೦Λߟྀͨ͠ύλʔϯ

    View full-size slide

  11. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 11
    ϔΩαΰφϧܥͰ͸ґଘؔ܎͕%*1ʹͳΔ
    w ґଘؔ܎ٯసͷݪଇ %*1%FQFOEFODZ*OWFSTJPO1SJODJQMF
    ΛϨΠϠʔԽ
    ΞʔΩςΫνϟʹద༻͠ԁঢ়ʹల։ͨ͠΋ͷɻυϝΠϯ૚͕Πϯϑϥετ
    ϥΫνϟ૚ʹʢ௚઀ʣґଘ͠ͳ͘ͳΔ

    View full-size slide

  12. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 12
    %*11PSU"EBQUPSͷ۩ମྫ
    w ಺ଆ͔Β֎ଆʹ͸௚઀ґଘ͕Ͱ͖ͳ͍ɻ

    View full-size slide

  13. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 13
    ࠓ·ͰͱԿ͕ҧ͏ͷ͔ʁ
    wυϝΠϯ૚͕֎ʹ࿙Εग़͞ͳ͍Α͏ʹ͢Δͱ͍͏
    ఺͕ڧௐ͞Ε͍ͯΔɻ֎ଆ͔Β಺ଆʹ͔͠௚઀త
    ʹґଘͰ͖ͳ͍ɻ಺ଆ͔Β֎ଆʹґଘ͢Δ৔߹͸*
    'Λ௨ͯؒ͠઀తʹґଘ͢Δɻ ࣮͸ɺ͜Ε͸ϨΠ
    ϠʔԽΞʔΩςΫνϟͰ΋ݴٴ͞Ε͍ͯΔ͜ͱ

    wݸਓతʹ͸ɺυϝΠϯ૚ͷՄൖੑΛҙࣝͯ͠ଟछ
    ଟ༷ͳΞμϓλʹରԠ͢Δߟ͑ํʹݟ͑Δɻ
    wͰ͸ɺՄൖɾඇՄൖͷ൑அج४ͱ͸Կ͔ʁ

    View full-size slide

  14. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 14
    υϝΠϯ૚ͷՄൖੑͱ͸
    w ೖग़ྗσόΠε͸ɺཁ݅ʹΑͬͯมΘΔͷ
    Ͱɺཁ݅ʹΑͬͯมΘΒͳ͍ϙʔτʹґଘ
    ͠ɺΞμϓλ૚͸੾ସՄೳʹɻ
    w υϝΠϯ૚ͷՄൖੑ͸ґଘઌʹىҼ͢Δ໰
    ୊ɻΠϯϑϥετϥΫνϟ૚ʹ4DBMB͚ͩ
    Ͱͳ͘"LLB΋ؚΊΔͱ͍͏ߟ͑ํ΋͋Δ͕ɺ
    *0σόΠεʹ͍ۙ෦෼Ͱ͸ϙʔτͷΑ͏
    ͳந৅ʹґଘґଘ͢΂͖ͩͱߟ͑Δɻ
    ΠϯϑϥετϥΫνϟ૚

    ཁ݅ʹΑͬͯ
    มΘΒͳ͍෦෼
    υϝΠϯ૚
    (Ϣʔεέʔε૚΋ྫ֎Ͱ͸ͳ͍)
    Ξμϓλ
    ཁ݅ʹΑͬͯ
    มΘΔ෦෼
    ϙʔτ(I/F)

    ཁ݅ʹΑͬͯ
    มΘΒͳ͍෦෼
    w ΠϯϑϥετϥΫνϟ૚͸ɺ্ҐͷϨΠϠʔΛࢧ͑ΔҰൠతͳٕज़తͳػೳΛఏڙ͢
    Δɻ͜Εʹ͸ɺΞϓϦέʔγϣϯͷͨΊͷϝοηʔδૹ৴ɺυϝΠϯͷͨΊͷӬଓ
    ԽɺϢʔβΠϯλʔϑΣΠεͷͨΊͷ΢ΟδοτඳըͳͲ͕͋Δɻ &WBOT

    w ϔΩαΰφϧܥ͸υϝΠϯ૚ͷՄൖੑΛҙࣝ͢ΔɻΠϯϑϥετϥΫνϟ૚͸ཁ݅ʹ
    ΑͬͯมΘΒͳ͍෦෼Ͱ͋ΔͨΊɺυϝΠϯ૚͸ΠϯϑϥετϥΫνϟ૚ʹґଘͯ͠
    ΋υϝΠϯ૚ͷՄൖੑ͸௿Լ͠ͳ͍ͱߟ͑ΒΕΔɻྫ͑͹ɺTDBMBMBOHͳͲɻ

    View full-size slide

  15. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 15
    $234ͱ͸
    w $PNNBOEBOE2VFSZ3FTQPOTJCJMJUZ4FHSFHBUJPO
    wίϚϯυɾΫΤϦ੹຿෼཭
    w ೥(SFH:PVOHࢯ͕ߟҊͨ͠ύλʔϯɻ
    w ೥ʹ#FSUSBOE.FZFSࢯ͕ߟҊͨ͠ίϚϯυΫΤϦ෼
    ཭ݪଇ $PNNBOE2VFSZ4FQBSBUJPO$24
    ΛΞʔΩς
    Ϋνϟʹద༻ͨ͠΋ͷ͕$234ɻ
    w ʮ͋ΒΏΔϝιου͸ɺΞΫγϣϯΛ࣮ߦ͢ΔίϚϯ
    υ͔ɺݺͼग़͠ݩʹσʔλΛฦ͢ΫΤϦ͔ͷ͍ͣΕ͔
    Ͱ͋ͬͯɺ྆ํΛߦͬͯ͸ͳΒͳ͍ɻ͜Ε͸ɺ࣭໰Λ
    ͢Δ͜ͱͰճ౴ΛมԽͤͯ͞͸ͳΒͳ͍ͱ͍͏͜ͱ
    ͩɻʯ

    View full-size slide

  16. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 16
    ୯७ͳ$234ΞʔΩςΫνϟ
    w $24ʹج͍ͮͨ࠷΋୯७ͳߏ੒͸ɺυϝΠϯ૚ΛϥΠτͱϦʔυͷܥ
    Λ෼͚Δɻ
    Client
    Write Stack Read Stack
    Database
    Domain Layer
    Application Layer
    Infrastructure Layer
    Domain Layer
    Application Layer
    Infrastructure Layer

    View full-size slide

  17. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 17
    ඪ४తͳ$234ΞʔΩςΫνϟ
    w ϥΠτͱϦʔυܥΛ͞Βʹ෼ੳ͍ͯ͘͠ͱɺϦʔυଆ͸ΫΤϦཁ݅ʹ
    ରԠ͢Δͷ͕໨తͳͷͰυϝΠϯϞσϧ͕ෆཁʹͳΔɻ
    Client
    Write Stack Read Stack
    Database
    Domain Layer
    Application Layer
    DAO + DTO
    Read Records
    Infrastructure Layer
    Write Records
    ReadModel Updater
    ܗࣜม׵Λߦ͏

    View full-size slide

  18. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 18
    ͳͥ$234ͳͷ͔ʁ
    wυϝΠϯϞσϧͷ୯७Խ
    wϦʔυଆʹ͸υϝΠϯϞσϧ͕ෆཁʹͳ
    ΔɻΫΤϦʹඞཁͳ%50͕͋Ε͹Α͍ɻ
    wϦʔυ͞ΕΔ͜ͱΛલఏʹू໿Λߟ͑Δ
    ඞཁ͕ͳ͍ɻ
    wϥΠτଆͷϞσϧ͸ৼΔ෣͍͚ͩʹ஫ྗ
    Ͱ͖ΔͷͰɺ୯७ͳ΋ͷʹͳΔɻ

    View full-size slide

  19. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 19
    %%%$234ΞʔΩςΫνϟ

    w ϦϙδτϦ͸TUPSF FOUJUZ
    SFTPMWF#Z JE
    ఔ౓ͷ࣮૷ͰΑ͍ɻ
    w ΑΓෳࡶͳΫΤϦཁ݅͸%"0%50Ͱ࣮ݱ͢Δɻ
    Domain Layer
    Repository(Id => Aggregate)
    Aggregate
    RootEntity
    Value Object
    DAO + DTO
    DAO
    findById
    findByName
    findByDeptId
    EmpDto
    { id, name, deptName }

    View full-size slide

  20. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 20
    38෼཭͚ͩͳΒ΄͔ʹ΋ํ๏͕͋Δ
    w υϝΠϯϞσϧͷࣸ૾Λ4MBWFʹ࡞Δ͚ͩɻͨͩ͠4MBWF%#͸ϨεϙϯεϞσϧͰ͸
    ͳ͍ͷͰɺ$MJFOUʹฦ͢લʹϨεϙϯεϞσϧͰ͋Δ%50ʹม׵͢Δඞཁ͕͋Δɻ
    Client
    Write Stack Read Stack
    Database
    Domain Layer
    Application Layer
    DAO + Converter + DTO
    Slave DB
    Infrastructure Layer
    Master DB

    View full-size slide

  21. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 21
    3ܥʹ͸4NBSU6*"OUJ1BUUFSO͕࢖͑Δ
    w υϝΠϯϞσϧ͸8ܥ͚ͩ
    ʹଘࡏ͢ΔͨΊɺ3ܥͰ͸
    4NBSU6*"OUJ1BUUFSO͕
    ར༻Ͱ͖Δɻ
    w %50͸Ϗϡʔ΍Ϩεϙϯ
    εΛҙࣝͨ͠ܗࣜΛ࠾༻
    Ͱ͖Δɻ
    w Ξϯνύλʔϯ΋෭࡞༻
    Λ੍ޚ͢Ε͹ޮྗΛൃش
    Client
    Read Stack
    Database
    DAO
    Read DB
    DTO as View or Response

    View full-size slide

  22. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 22
    $234ΞʔΩςΫνϟΛ࠾༻͢ΔϝϦοτ
    w ઃܭ͕γϯϓϧʹͳΔ
    w 38ʹ͓͍ͯ૬ޓʹແବͳૢ࡞Λ࣮ߦ͠ͳ͍Α͏ʹ͢Δ͜ͱ͕೉͍͕͠ɺίϚϯυ
    ͱΫΤϦΛ෼཭͢Δ͜ͱͰ͜ͷෳࡶ͞ΛܰݮͰ͖Δɻ
    w ϓϩδΣΫγϣϯ͕͋Ε͹ϦʔυଆͷΠϯϐʔμϯεϛεϚονͷղফ͕ෆཁͱͳ
    Δɻ
    w υϝΠϯϞσϧΛλεΫॏࢹͰߟ͑ΒΕΔΑ͏ʹͳΔ
    w ৼΔ·͍ λεΫ
    Λத৺ʹ͓͍ͨυϝΠϯϞσϦϯά͕Ͱ͖ΔΑ͏ʹͳΔɻ
    w ίʔυ͕ཧղ͠΍͘͢ͳΔ
    w ϥΠτͱϦʔυͦΕͧΕʹγεςϜͷܥ͕෼͔ΕΔͷͰίʔυ͕ಡΈ΍͘͢ͳΔɻ
    w ϦάϨογϣϯ͠ʹ͍͘
    w ยํͷܥͷมߋ͕΋͏ҰํͷܥʹӨڹΛ༩͑ʹ͍͘ɻϦάϨογϣϯͷϦεΫΛܰ
    ݮͰ͖Δɻ
    w εέʔϥϏϦςΟ΁ͷد༩
    w Ϣʔβ਺͕૿͑ͯ΋ಉ͡ϨϕϧͰύϑΥʔϚϯεΛҡ࣋͢ΔͨΊʹɺ38ͦΕͧΕ
    ʹಛੑͷҧ͏࠷దԽ͕͠΍͘͢ͳΔɻ ྫඇಉظॻ͖ࠐΈ΍ϦʔυΩϟογϡͳͲ

    View full-size slide

  23. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 23
    &WFOU4PVSDJOHͱ͸Կ͔ʁ
    w (SFH:PVOHࢯߟҊ
    w &WFOU4PVSDJOH ҎԼ&4
    ͱ͸ɺσʔλͰ͸ͳ͘Կ͔͠Βͷग़དྷࣄʹυϝ
    ΠϯΠϕϯτΛϞσϦϯάͷओ໾ͱ͢Δ͜ͱɻ
    w υϝΠϯϞσϧΛσʔλͱͯ֨͠ೲ͢ΔͷͰ͸ͳ͘ɺൃੜ͢ΔυϝΠϯΠ
    ϕϯτΛ͢΂ͯӬଓԽ͢Δɻ جຊతʹ௥Ճ͔͠͠ͳ͍

    w &4͸$234Λܶతʹվྑ͢Δ ҎԼ$234&4
    ɻ
    CartItemAdded { id, cartId, itemId, itemCount }
    CartItemCountUpdated { id, cartId, itemId, itemCount }
    CartCreated { id, cartId, userId }
    CartItemRemoved { id, cartId, itemId }

    View full-size slide

  24. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 24
    4UBUF4PVSDJOH͔Β&WFOU4PVSDJOH΁
    w 4UBUF4PVSDJOH ҎԼ44

    w σʔλϕʔεΛεφοϓγϣοτͱͯ͠දݱ $36%ͳͲ
    ɻৗʹ.VUBUJPOΛൃੜͤ͞ɺ࠷ޙ
    ʹ֬ೝ͞Εͨਖ਼ৗͳঢ়ଶΛ֨ೲ͢Δ ʮ࠷ޙʹ֬ೝ͞Εͨਖ਼ৗͳঢ়ଶʯΞϓϩʔν
    ɻ
    w &WFOU4PVSDJOH
    w Ϗδωεϧʔϧ్͕தͰมߋ͞ΕΕΔͱաڈ͸ࣦΘΕΔɻΞʔΩςΫνϟΛޙ͔Βमਖ਼͢
    Δ৔߹ɺগྔͷෆ଍৘ใͰ΋େ͖ͳมߋίετΛ෷͏ඞཁ͕͋Δɻͦ΋ͦ΋εφοϓ
    γϣοτϕʔεͷσʔλදݱ͸ɺΠϕϯτϕʔεͷσʔλදݱͰ͋ΔණࢁͷҰ֯ʹա͗ͳ
    ͍ɻ
    w &4Ͱ͸ʮൃੜͨ͠ΠϕϯτʯΞϓϩʔνͰ͋Γɺओཁͳσʔλιʔε͸υϝΠϯΠϕϯτ
    ʹɻυϝΠϯΠϕϯτ͸ෆมͰ͋ΔͨΊΠϕϯτσʔλϕʔεΛϦϓϦέʔγϣϯ͢Δͷ͸
    ൺֱత؆୯ɻ͜Ε͸εέʔϥϏϦςΟʹେ͖ͳӨڹΛ༩͑Δɻ
    w υϝΠϯΠϕϯτ͕͋Ε͹͢΂͕ͯखʹೖΔɻϦʔυϞσϧ͸&WFOUΛϦϓϨΠ͠ߏங͢Δ
    ͜ͱ͕Ͱ͖ΔɻυϝΠϯΠϕϯτ͔ΒεφοϓγϣοτΛߏஙͰ͖Δɻ
    Event Sourcing
    State Sourcing
    εφοϓγϣοτϕʔε
    ͷσʔλදݱ(DB)
    Πϕϯτϕʔε
    ͷσʔλදݱ

    View full-size slide

  25. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 25
    ':*$234&4Λద༻ͨ͠ϑϨʔϜϫʔΫ
    w +BWB4DSJQU
    w 'BDFCPPL'MVY
    w 3FEVY
    w 3FqVY+4
    w +BWB
    w "YPO'SBNFXPSL
    w 3VCZ
    w 4FRVFOU
    w 4DBMB
    w -BHPN
    ίϚϯυଆ
    ΫΤϦଆ
    Fluxͷ৔߹

    View full-size slide

  26. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 26
    ':*-BHPNͱ͸
    w -JHIUCFOEࣾ چUZQFTBGFࣾ
    ͕ఏڙ͢ΔϚΠΫϩαʔϏεϑϨʔϜϫʔΫ
    w IUUQTXXXMJHIUCFOEDPNMBHPN
    w IUUQTHJUIVCDPNMBHPNMBHPN
    w $234&4Λαϙʔτ
    w ελοΫߏ੒
    w QMBZGSBNFXPSL
    w BLLB
    w BLLBBDUPS
    w BLLBIUUQ
    w BLLBTUSFBN
    w BLLBQFSTJTUFODF
    w BLLBQFSTJTUFODFRVFSZ
    w BLLBDMVTUFS

    View full-size slide

  27. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 27
    $234&4ΞʔΩςΫνϟ

    w γεςϜ͸ू໿Ͱൃੜͨ͠υϝΠϯΠϕϯτͰۦಈ͠Πϕϯτ͸δϟʔφϧ
    ͱͯ͠ӬଓԽ͞ΕΔɻΠϕϯτ͔ΒͷϦϓϨΠ͸4OBQTIPU+PVSOBMɻ
    Client
    Write Stack Read Stack
    Data Store
    DAO + DTO
    Read Records
    Aggregate
    Application Layer
    Infrastructure Layer
    Event Store
    Journals Snapshots
    Πϕϯτͷอଘઌ
    CartCreated
    CartItemAdded

    ͢΂ͯͷΠϕϯτΛ
    ӬଓԽ
    Snapshot+journalsͰ
    ϦϓϨΠ

    View full-size slide

  28. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 28
    %%%$234&4ΞʔΩςΫνϟ

    w ίϚϯυʹΑͬͯੜͨ͡ू໿ͷঢ়ଶมԽΛΠϕϯτͱ͠
    ͯ௨஌͢Δɻ
    Domain Layer
    Aggregate
    Aggregate Aggregate
    Command Command
    Event
    Command/Event Handler
    Event Event
    Event
    Command
    Command/Event Handler
    Command/Event Handler
    Event Event
    Event

    View full-size slide

  29. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 29
    υϝΠϯΠϕϯτͱϚΠΫϩαʔϏε
    w #$ؒͷ࿈ܞʹ΋υϝΠϯΠϕϯτ͕ར༻ՄೳʹͳΔɻϚΠΫϩαʔϏεΞʔΩ
    ςΫνϟͱ૬ੑ͕Α͍ɻ
    w z֎෦ͷଞͷڥք͚ͮΒΕͨίϯςΩετʹ͸໌ࣔతͳΠϯλʔϑΣΠε͕͋
    ΓɺͦͷΠϯλʔϑΣΠε͕ଞͷίϯςΩετͱڞ༗͢ΔϞσϧΛܾఆ͠·
    ͢ɻz ϚΠΫϩαʔϏεΞʔΩςΫνϟΑΓ

    w Πϕϯτʹ͸#$಺෦͚ͩͰൃੜɾফඅ͞ΕΔӅΕΠϕϯτͱ֎෦ͷ#$͕ؔ৺
    Λ࣋ͭڞ༗Πϕϯτ͕ଘࡏ͢Δɻ
    BC (2)
    Application
    Domain
    Layer
    Aggregate
    BC (1)
    Application
    Domain
    Layer
    Aggregate
    BC (3)
    Application
    Domain
    Layer
    Aggregate
    Event
    Event Event

    View full-size slide

  30. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 30
    ίϚϯυͱΠϕϯτͷؔ܎
    w ू໿͸ίϚϯυΛૢ࡞ΠϯλʔϑΣΠεͱͯ͠ड͚෇͚Δɻ
    w ίϚϯυ͸ϦΫΤετͱϨεϙϯεʹ෼͚Δ͜ͱ΋Ͱ͖Δɻ
    w ू໿ͷ಺෦ঢ়ଶΛม͑ΔίϚϯυ͸ɺΠϕϯτΛൃੜͤ͞Δɻ
    w Πϕϯτૹड৴͸1VCMJTIFS ૹ৴ଆ
    ͱ4VCTDSJCFS ड৴ଆ
    ʹ෼
    ͔ΕΔɻ྆ऀͷؔ࿈͸1VCMJTIFS4VCTDSJCFS/ͱͳΔɻ
    Aggregate
    Command(Request/Response)
    Event Handler
    Any
    Any
    Event Handler
    Event
    Publisher
    Subscriber
    Subscriber
    Command Handler

    View full-size slide

  31. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 31
    Πϕϯτόε
    w Πϕϯτόε͸1VCMJTIFS4VCTDSJCFSΛૄ݁߹ʹ͢ΔͨΊͷϞσ
    ϧɻτϐοΫϕʔεͰ࣮૷͞ΕΔ͜ͱ͕ଟ͍ɻ
    w 4VCTDSJCFS͸ؔ৺͕͋ΔτϐοΫͱࣗ෼ࣗ਎Λ&WFOU#VTʹొ࿥͢
    Δɻ1VCMJTIFS͸τϐοΫʹରͯ͠&WFOUΛૹ৴͢Δɻ
    w "LLBʹ͸&WFOU#VTͷ࣮૷͕ෳ਺͋Δ &WFOU4USFBN %JTUSJCVUFE
    1VC4VC
    ɻଞʹ΋,BGLBͳͲ΋બ୒ࢶʹͳΔɻ
    Aggregate
    Command(Request/Response)
    Event Handler
    Any
    Any
    Event Handler
    Publisher
    Subscriber
    Subscriber
    Command Handler
    EventBus eventBus.subscribe(topic, subscriber)
    eventBus.subscribe(topic, event)

    View full-size slide

  32. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 32
    &4ʹ͸ܽ఺͸ͳ͍ͷ͔ʁ
    w *%&΍ϑϨʔϜϫʔΫ͕44Λલఏʹ͍ͯ͠Δ΋ͷ͕ଟ͍ɻ
    ͜ͷཱ৔͔Β͸શ͘σʔλΛอଘ͠ͳ͍Α͏ʹݟ͑Δɻ Ͱ
    ΋"LLBͳΒେৎ෉

    w Πϕϯτͷܗࣜ͸ɺϏδωεϧʔϧ͕มԽͨ࣌͠ʹεΩʔ
    Ϛ͕มԽ͢ΔͨΊϦϨʔγϣφϧσʔλϕʔεʹ͸޲͔
    ͳ͍ɻ,74͕޲͍͍ͯΔɻ
    w Ϗδωε্Ͱى͜ΔΠϕϯτΛҰͭ࢒ΒͣӬଓԽ͢Δͨ
    Ίɺण໋͕௕͍ΤϯςΟςΟΛѻ͏Ϣʔεέʔεʹ޲͍
    ͍ͯΔ ΩϟύγςΟ͕ڐͤ͹
    ɻ
    w ͳΜͰ΋͔ΜͰ΋&4ʹ͸Ͱ͖ͳ͍͜ͱ΋͋Δɻ$234Ͱ΋
    44Λબ୒Ͱ͖ͨํ͕͍͍͕ݱ࣮͸೉͍͔͠΋͠Εͳ͍ɻ

    View full-size slide

  33. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 33
    "LLBͰͲͷΑ͏ʹ࣮૷͢Δͷ͔ʁ
    8ܥத৺ͷղઆͰ͢

    View full-size slide

  34. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 34
    "LLBͬͯԿʁ
    w IUUQBLLBJP
    w ݱMJHIUCFOEࣾͷ$50+POBT#POFSࢯ͕։ൃɻ
    w &SMBOH͔Βͷ༌ೖ
    w ෼ࢄγεςϜΛ؆୯ʹ࡞ΕΔ εέʔϧΞοϓ εέʔϧ
    Ξ΢τઓུΛಉ͡ϓϩάϥϛϯάϞσϧͰαϙʔτ͢Δ

    w ಛ௃
    w ඇಉظɾϊϯϒϩοΩϯά
    w ࣗݾ࣏༊ೳྗΛ࣋ͭ 3FTJMJFOUCZ%FTJHO

    w ଎͍ɾޮ཰త
    w ΫϥελΛߏஙͰ͖Δɻ41P'͕ͳ͍ɻ

    View full-size slide

  35. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 35
    ͳͥ"LLBͳͷ͔ʁ ٕज़తͳଆ໘

    w $,໰୊ͷղܾ
    w ૿͑ଓ͚ΔΫϥΠΞϯτΛͲͷΑ͏ʹࡹ͔͘ɻ໰୊ʹͳΔͷ͸ϒϩο
    Ωϯά*0 ΠϕϯτϧʔϓͱϊϯϒϩοΩϯά*0OHOJY OPEFKTͷ
    ୆಄

    w $16ةػ
    w ΫϩοΫ਺Λ͋͛Δ͜ͱ͕ݶքʹͳΓγϯάϧίΞةػɻϜʔΞͷ๏
    ଇ τϥϯδελͷूੵີ౓͸೥͝ͱʹഒʹͳΔ
    Λҡ࣋͢ΔͨΊʹ
    ϚϧνίΞԽ΁ɻ
    w ͦͯ͠ϚϧνίΞةػ͕౸དྷɻΞϓϦέʔγϣϯ͕ฒߦ౓͕௿͍ͱί
    ΞΛੜ͔͠੾Εͳ͍ ΞϜμʔϧͷ๏ଇ
    ɻ୯ҰίΞ͸௿଎ʹͳΔͨΊ
    ϚϧνίΞʹରԠ͠ͳ͍ͱύϑΥʔϚϯε͕௿Լ͠΍͍͢ɻ͔͠͠ɺ
    εϨουϓϩάϥϛϯά͸ඇৗʹ೉͍͠ɻσουϩοΫɺϨʔείϯ
    σΟγϣϯɺՄࢹੑ໰୊ͳͲɻ

    View full-size slide

  36. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 36
    ͳͥ"LLBͳͷ͔ʁ ٕज़తͳଆ໘

    w ϚϧνίΞةػΛλʔήοτʹͨ͠4DBMBͱ&SMBOH
    w ෆมੑ΍ܰྔϓϩηε ΞΫλʔͳͲ

    w &SMBOHͷ఻આ
    w ೥ʹ&SJDTTPO͕ΞΫλʔϞσϧʹج͍ͮͨݴޠ&SMBOHΛ։ൃɻ
    &SJDTTPOͷ"9%ి࿩ަ׵ػ͕ OJOFOJOFT
    ೥ؒͰఀࢭ͕࣌ؒඵ
    ͷՄ༻ੑΛୡ੒ɻ೥8IBUT"QQ
    ͷ&SMBOHγεςϜ͕̍୆ͷαʔόʔͰສΫϥΠΞϯτΛࡹ
    ͘ɻ
    w "LLBͷొ৔
    w ೥"LLBͷϦϦʔε &SMBOH͔ΒΠϯεύΠΞ
    ɻϝοηʔ
    δύογϯά΋*0΋ϊϯϒϩοΩϯάͰ$,໰୊Λղܾɻ
    w "LLBͷύϑΥʔϚϯε͕&SMBOHΛѹ౗͢Δɻ

    View full-size slide

  37. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 37
    ͳͥ"LLBͳͷ͔ʁ %%%తͳଆ໘

    w %%%
    w ू໿ΛΞΫλʔͱ࣮ͯ͠૷Ͱ͖Δɻ"LLB$MVTUFS4IBSEJOHΛద༻͢
    ΔͱɺϊʔυؒΛލ͙ू໿ͱ࣮ͯ͠૷Մೳɻ
    w $234&4
    w "LLB1FSTJTUFODF 1FSTJTUFOU"DUPS
    Λ࢖ͬͯυϝΠϯΠϕϯτΛӬଓ
    ԽͰ͖Δɻ·ͨঢ়ଶͷ෮ݩ΋Մೳɻ
    w "LLB1FSTJTUFODF2VFSZΛ࢖ͬͯϦʔυϞσϧΛߏஙͰ͖Δɻ
    w υϝΠϯΠϕϯτΛϝοηʔδύογϯάͰ͖Δɻ
    &WFOU#VT &WFOU4USFBN %JTUSJCVUFE1VC4VCͳͲ
    Ͱ1VC4VCϞσ
    ϧ΋ར༻Ͱ͖Δɻ
    w ΞϓϦέʔγϣϯcυϝΠϯ
    αʔϏεΛ1FSTJTUFODF"DUPS΍
    1FSTJTUFOU'4.ͳͲΛ࢖ͬͯϓϩηεϚωʔδϟͱ࣮ͯ͠૷Ͱ͖Δɻ

    View full-size slide

  38. ΠϯλʔϑΣΠε૚
    Ϣʔεέʔε૚
    ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 38
    ΫϦʔϯΞʔΩςΫνϟXJUI"LLB
    w "LLBͰΫϦʔϯʹ͢Δ৔߹ͷϨΠΞ΢τͷҰྫ
    ίϚϯυ
    UseCase
    (CreateUser, UpdateUserName, GetUser)
    Persistent
    Actor
    <>

    akka-http
    <<(In|Out)putPort>>

    akka-persistence
    ΫΤϦ
    <<(In|Out)putPort>>

    UserDao
    UserDao
    Impl
    <>

    DBMS
    ௚઀ґଘ
    ActorRefʹґଘ͠ͳ͘ͳ͍৔߹

    trait UserPort {
    def createUser(cmd): Future[Response] =
    ref ? cmd

    }
    ܧঝ
    <<(In|Out)putPort>>

    ActorRef
    ؒ઀ґଘ
    ௚઀ґଘ
    (In|Out)putPort
    Flow[Request,Response]

    • υϝΠϯ૚͸ɺΠϯϑϥ૚(Scala)ʹ͚ͩґଘ͢Δɻ
    • Ϣʔεέʔε૚͸ɺυϝΠϯ૚ͱΠϯϑϥ૚(Scala, Akka)ʹґଘ͢Δɻ
    • ΠϯλʔϑΣΠε૚͸ɺϢʔεέʔε૚ͱΠϯϑϥ૚(Scala, Akka)ʹґଘ͢Δɻ
    υϝΠϯ૚
    UserAggregateRoot
    ௚઀ґଘ
    ܧঝ
    <>

    akka-http
    Route + Json Route + Json
    ௚઀ґଘ ௚઀ґଘ

    View full-size slide

  39. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 39
    ΞΫλʔͱ͸Կ͔ʁ
    w8JLJQFEJBΑΓҾ༻
    wΞΫλʔϞσϧͷجຊ͸ʮશͯͷ΋ͷ͸ΞΫλʔͰ͋Δʯͱ͍͏఩
    ֶͰ͋Δɻ͜Ε͸ΦϒδΣΫτࢦ޲ϓϩάϥϛϯάʹ͓͚Δʮશͯͷ
    ΋ͷ͸ΦϒδΣΫτͰ͋Δʯͱ͍͏ߟ͑ํͱࣅ͍ͯΔ͕ɺΦϒδΣΫ
    τࢦ޲ιϑτ΢ΣΞͰ͸جຊతʹஞ࣍తʹ࣮ߦ͢Δͷʹରͯ͠ɺΞΫ
    λʔϞσϧͰ͸ຊ࣭తʹฒߦੑΛඋ͍͑ͯΔ఺͕ҟͳΔɻ
    wΞΫλʔ͸ฒߦతʹड৴͢ΔϝοηʔδʹରԠͨ͠ҎԼͷΑ͏ͳৼ
    Δ෣͍Λඋ͑ͨܭࢉ࣮ମʢ$PNQVUBUJPOBM&OUJUZʣͰ͋Δ
    w ଞͷ
    ΞΫλʔʹ༗ݶݸͷϝοηʔδΛૹ৴͢Δɻ
    w༗ݶݸͷ৽ͨͳΞΫλʔΛੜ੒͢Δɻ
    w࣍ʹड৴͢Δϝοηʔδʹର͢Δಈ࡞Λࢦఆ͢Δɻ
    w͜ΕΒͷৼΔ෣͍ʹ͸ஞ࣍ੑ͸લఏͱ͞Ε͓ͯΒͣɺฒྻతʹ͜
    ΕΒΛ࣮ߦ͢Δɻ

    View full-size slide

  40. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 40
    class MyActor extends Actor {

    val log = Logging(context.system, this)

    def receive = {

    case "test" => log.info("received test")

    case _ => log.info("received unknown message")

    }

    }


    object Main extends App {

    val system = ActorSystem()

    val actorRef = system.actorOf(Props[MyActor])

    actorRef ! "test"

    actorRef ! "hello"

    Await.result(system.terminate(), Duration.Inf)

    }

    "LLB"DUPSͷαϯϓϧ

    View full-size slide

  41. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 41
    ௥Ճ͢Δґଘؔ܎
    w BLLBBDUPS
    w BLLBQFSTJTUFODF
    w εςʔτϑϧΞΫλʔͷ಺෦ঢ়ଶΛӬଓԽ͢Δػೳɻ
    ΞΫλʔͷॳظԽ࣌΍ྫ֎ʹΑΔ࠶ىಈ࣌ɺ+7.ͷΫ
    ϥογϡ࣌ʹҎલͷঢ়ଶΛ෮ݩ͢Δ͜ͱ͕Ͱ͖Δ
    w ΞΫλʔͷ಺෦ঢ়ଶ͸ӬଓԽ͞ΕΔ͕ɺܾͯ͠ݱࡏͷ௚
    ઀తͳঢ়ଶΛѻ͏Θ͚Ͱ͸ͳ͍ɻ

    View full-size slide

  42. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 42
    ू໿Λ1FSTJTUFOU"DUPSͰ࣮૷͢Δ
    class UserAggregate(userId: UserId, eventBus: EventBus)
    extends PersistentActor {


    var state: Option[User] = None

    def initialized = state.isDefined

    override def persistenceId: String = userId.toPath

    override def receiveRecover: Receive = {

    case event: UserInitialized =>

    state = Some(User.applyState(event))

    case event: UserNameUpdated =>

    state = state.map(_.updateState(event))

    case event: UserDestroyed =>

    state = state.map(_.updateState(event))

    }

    override def receiveCommand: Receive = {

    case cmd: InitializeUser =>

    require(initialized)

    persist(cmd.toEvent) { event =>

    state = Some(User.applyState(event))
    sender() ! CreateUserSucceeded(UUID.randomUUID, cmd.id)

    eventBus.publish(Topic(event), event)

    }

    case cmd: UpdateUserName =>

    require(!initialized)

    persist(cmd.toEvent) { event =>

    state = state.map(_.updateState(event))
    sender() ! UpdateUserNameSucceeded(UUID.randomUUID, cmd.id)

    eventBus.publish(Topic(event), event)

    }

    case cmd: DestroyUser =>

    require(!initialized)

    persist(cmd.toEvent) { event =>

    state = state.map(_.updateState(event))

    sender() ! DestroyUserSucceeded(UUID.randomUUID, cmd.id)

    eventBus.publish(Topic(event), event)

    }

    }

    }
    w SFDFJWF$PNNBOE͸ίϚϯυϋϯυ
    ϥɻ
    w QFSTJTUͰυϝΠϯΠϕϯτΛӬଓԽ͢
    Δɻ 3FQPTJUPSZTUPSF૬౰

    w QFSTJTUޙʹ
    w ू໿ϧʔτͷঢ়ଶΛมߋ͢Δɻ
    w TFOEFSʹ"DL/BDLΛૹ৴͢Δɻ
    w υϝΠϯΠϕϯτΛ4VCTDSJCFSʹ
    ૹ৴͢Δɻ /

    w SFDFJWF3FDPWFS͸"DUPSىಈ࣌ʹू໿
    ϧʔτͷঢ়ଶ෮ݩΛߦ͏ͨΊͷϝιο
    υ 3FTQPTJUPSZSFTPMWF#Z JE
    ૬౰ɻ
    &WFOU͔Β4UBUFΛಘΔ
    ɻ
    w 4OBQTIPUΛ࢖͏ͱϦϓϨΠ͕ߴ଎ԽͰ
    ͖Δɻ
    w 1FSTJTUFOU"DUPSͰू໿3FQPTJUPSZ૬
    ౰ͷػೳΛ࣮ݱՄೳɻ

    View full-size slide

  43. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 43
    ίϚϯυͷ࣮૷
    w ίϚϯυϦΫΤετΛ
    ࣮૷͢Δɻ
    w ϝιουۦಈܕͰ͍͏
    ͱϝιου໊ͱҾ਺ͷ
    ू߹Λ·ͱΊͨ΋ͷ͕
    ίϚϯυͱͳΔɻ
    w ίϚϯυ͔ΒΠϕϯτ
    Λੜ੒Ͱ͖ΔΑ͏ʹ͢
    Δɻ
    object UserProtocol {

    object Commands {

    trait UserCommandRequest extends CommandRequest {

    val userId: UserId

    }

    case class InitializeUser(

    identifier: CommandRequestId = CommandRequestId(),

    userId: UserId,

    name: String

    ) extends UserCommandRequest with InitializeCommandRequest {

    override def toEvent: UserInitialized =

    UserInitialized(EventId(), userId, name)

    }

    case class UpdateUserName(

    identifier: CommandRequestId = CommandRequestId(),

    userId: UserId,

    name: String

    ) extends UserCommandRequest with UpdateCommandRequest {

    override def toEvent: UserNameUpdated =

    UserNameUpdated(EventId(), userId, name)

    }

    case class DestroyUser(

    identifier: CommandRequestId = CommandRequestId(),

    userId: UserId

    ) extends UserCommandRequest with DestroyCommandRequest {

    override def toEvent: UserDestroyed =

    UserDestroyed(EventId(), userId)

    }

    }

    }

    View full-size slide

  44. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 44
    Πϕϯτͷ࣮૷
    w ίϚϯυΛड͚෇͚ͨ
    ޙʹൃੜ͢ΔΠϕϯτ
    Λ࣮૷͢Δɻ
    w Πϕϯτ͸ΤϯςΟςΟ
    ͷҰछͰ͋ΔͨΊɺԿ
    ͔͠Βͷࣝผํ๏Λඋ
    ͑Δɻ
    w Πϕϯτ͸"LLB
    1FSTJTUFODFͰӬଓԽ͞
    ΕΔɻ
    object UserProtocol {


    object Events {

    trait UserEvent extends Event


    case class UserInitialized(

    identifier: EventId = EventId(),

    userId: UserId,

    name: String,

    createAt: DateTime = DateTime.now

    ) extends InitializedEvent with UserEvent


    case class UserNameUpdated(

    identifier: EventId = EventId(),

    userId: UserId,

    name: String,

    createAt: DateTime = DateTime.now

    ) extends UpdatedEvent with UserEvent


    case class UserDestroyed(

    identifier: EventId = EventId(),

    userId: UserId,

    createAt: DateTime = DateTime.now

    ) extends DestroyedEvent with UserEvent


    }
    }

    View full-size slide

  45. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 45
    &WFOU#VTͷ࣮૷
    w "LLB&WFOU4USFBNͰͷ࣮
    ૷ɻϩʔΧϧ༻ Ϋϥελ
    ্ͷଞͷϊʔυʹΠϕϯτ
    Λ഑ૹͰ͖ͳ͍
    ɻ
    w 4VCTDSJCFS͸τϐοΫʹର
    ͯ͠4VCTDSJCF͢Δɻ
    w 1VCMJTIFS͸4VCTDSJCFSΛࢦ
    ఆͤͣʹΠϕϯτΛ1VCMJTI
    ͢Δɻ
    w ཻ౓͕େ͖͍5QPJDͷΠϕ
    ϯτΛߪಡ͢ΔͱΠϕϯτ
    ͷॲཧ͕௥͍͔ͭͳ͘ͳ
    Δɻ
    w ࣮ࡍ͸BLLBFWFOU&WFOU#VT
    Λ࣮૷͢ΔͱΑ͍ɻ
    trait EventBus {

    def subscribe(topic: Class[_], subscriber: ActorRef): Unit

    def publish(event: Event): Unit

    }


    class EventBusImpl(system: ActorSystem) extends EventBus {


    override def subscribe(topic: Class[_],
    subscriber: ActorRef): Unit = {

    system.eventStream.subscribe(subscriber, topic)

    }


    override def publish(event: Event): Unit ={

    system.eventStream.publish(event)

    }


    }
    object EventBus {


    def ofEventStream(system: ActorSystem): EventBus =
    new EventBusImpl(system)


    }


    View full-size slide

  46. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 46
    υϝΠϯαʔϏεͱͯ͠ͷ1SPDFTT.BOBHFS
    w ޱ࠲ؒసૹͷυϝΠ
    ϯαʔϏεɻ
    w #BOL"DDPVOUؒͰ͓
    ۚΛసૹ͢Δɻ
    w 1FSTJTUFODF'4.಺
    ෦ঢ়ଶ͕ӬଓԽՄೳ
    ͳ༗ݶεςʔτϚγ
    ϯɻൃੜ͢ΔυϝΠϯ
    ΠϕϯτΛӬଓԽ͢
    Δɻ
    class TransferDomainService(id: UUID) extends PersistentFSM[State, Data, Event] {

    override def persistenceId: String = id.toString

    override def domainEventClassTag: ClassTag[Event] = classTag[Event]

    override def applyEvent(domainEvent: Event, currentData: Data): Data = 

    domainEvent match {

    case Transferred(id, money, from, to, ref) => TransferData(id, money, from, to, ref)

    case Transferring(id, money, from, to, ref) => TransferData(id, money, from, to, ref)

    }


    startWith(Stopped, Empty)

    when(Stopped) {

    case Event(Transfer(requestId, money, from, to), _) =>

    context.system.eventStream.subscribe(self, classOf[Decreased])

    val ev = Transferring(UUID.randomUUID(), money, from, to, sender())

    goto(Started) applying ev andThen {

    case d: TransferData =>

    from ! Decrease(UUID.randomUUID(), money)

    sender() ! CommandSucceeded(UUID.randomUUID(), requestId)

    context.system.eventStream.publish(ev)

    }

    }

    when(Started) {

    case Event(Decreased(_, _), _) =>

    goto(FromDecreased) andThen {

    case d@TransferData(_, money, from, to) =>

    context.system.eventStream.unsubscribe(self, classOf[Decreased])

    context.system.eventStream.subscribe(self, classOf[Increased])

    to ! Increase(UUID.randomUUID(), money)

    }

    case ev@Event(CommandSucceeded(_, _),_) =>

    stay

    }

    when(FromDecreased) {

    case Event(Increased(_, _), TransferData(_, money, from, to, ref)) =>

    goto(Stopped) applying Transferred(UUID.randomUUID(), money, from, to, ref) andThen {

    case d: TransferData =>

    context.system.eventStream.unsubscribe(self, classOf[Increased])

    context.system.eventStream.publish(Transferred(UUID.randomUUID(), money, from, to))

    }

    case ev@Event(CommandSucceeded(_, _),_) =>

    stay

    }

    initialize()

    }

    View full-size slide

  47. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 47
    ௥Ճ͢Δґଘؔ܎
    w BLLBQFSTJTUFODFRVFSZ
    w ඇಉظετϦʔϜϕʔεͷΫΤϦ*'ʹΑͬͯఏڙ͞ΕΔΫΤϦʔίϯ
    ϙʔωϯτɻ
    w BLLBTUSFBN
    w ΞΫλʔΛͭͳ͗߹ΘͤΔͱඇಉظετϦʔϜॲཧΛ͢ΔͨΊͷ1JQF
    BOE'JMUFSΛ࡞Δ͜ͱ͕Ͱ͖Δ͕ɺ҆ఆͨ͠ετϦʔϛϯάॲཧΛ͢Δ
    ʹ͸ɺΤϥʔϋϯυϦϯάΛߦͬͨΓɺ͋Δϓϩηε্ͷόοϑΝ΍
    ϝʔϧϘοΫε͕Φʔόʔϑϩʔ͠ͳ͍Α͏ʹ͢Δ CBDLQSFTTVSF

    ཁ͕͋Δɻ
    w ੩తܕͳཁૉΛѻ͏ετϦʔϜͰΞΫλʔ͸഑ઢϛεΛ๷ࢭ͢Δ੩త
    ܕ෇͚Λอূ͢Δํ๏͕ͳ͍ɻ
    w BLLBTUSFBN͸্هͷ໰୊Λղܾ͢Δɻ

    View full-size slide

  48. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 48
    BLLBTUSFBN
    w 4PVSDF<0VU .BU>ܕ͸ɺετϦʔϜͷ্ྲྀͰσʔλΛఏڙ͢
    ΔɻҰͭ໨ͷܕҾ਺͸ιʔε͕Լྲྀʹఏڙ͢Δ஋ͷܕͰɺೋͭ
    ໨ͷܕҾ਺͸ɺιʔε࣮ߦ࣌ʹ͍͔ͭ͘ͷิॿ஋ΛఏڙͰ͖Δ ͨ
    ͱ͑͹ɺωοτϫʔΫιʔε͸ɺϐΞΞυϨε΍ό΢ϯυϙʔ
    τʹؔ͢Δ৘ใΛఏڙ͢Δ͔΋͠Εͳ͍
    ɻ
    w 4JOL<*O .BU>ܕ͸ɺετϦʔϜͷԼྲྀͰσʔλΛফඅ͢Δɻ
    Ұͭ໨ͷܕҾ਺͸্ྲྀ͔Βड͚औΔ஋ͷܕɻೋͭ໨ͷยҾ͖਺
    ͸ಉ͡ɻ
    w 'MPX<*O 0VU .BU>ܕ͸ɺ4PVSDFˠ'MPXˠ4JOLͷΑ͏ʹ
    ετϦʔϜͷதྲྀͰɺ্ྲྀ͔Βͷ஋ΛԼྲྀʹఏڙ͢Δɻ

    View full-size slide

  49. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 49
    4USFBN4PVSDFˠ'MPXˠ4JOL
    w όοΫϓϨογϟ෇͖ͷετϦʔϜॲཧ͕Մೳɻ
    w ೖྗ஋ͷܕʹΑΒͣ౷Ұతʹ4PVSDFܕ͕ར༻Ͱ͖Δɻ
    Source[A, M] Flow[A, B, M] Sink[B, M]
    data data
    Request
    Request
    • Source#single[T](element: T)
    • Source#apply[T](iterable: Iterable[T])
    • Source#maybe[T]
    • Source#fromFuture[T](future: Future[T])
    • Source#actorPublisher(props: Props)

    View full-size slide

  50. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 50
    BLLBTUSFBNͷαϯϓϧ
    w ετϦʔϜͰ͸ೖྗ஋Λݸʑʹॲཧ͢Δ
    val n = (1 to 10)

    .map { i => println(s"A: $i"); i }

    .map { i => println(s"B: $i"); i }

    .map { i => println(s"C: $i"); i }

    .sum
    A: 1
    A: 2

    A: 9
    A: 10
    B: 1
    B: 2

    B: 9
    B: 10
    C: 1
    C: 2

    C: 9
    C: 10
    n = 55
    val f = Source(1 to 10)

    .map{ i => println(s"A: $i"); i }

    .map{ i => println(s"B: $i"); i }

    .map{ i => println(s"C: $i"); i }

    .runWith(Sink.fold(0){ (l, e) => l + e})
    A: 1
    B: 1
    C: 1
    A: 2
    B: 2
    C: 2


    A: 9
    B: 9
    C: 9
    A: 10
    B: 10
    C: 10
    n2 = 55

    View full-size slide

  51. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 51
    BLLBTUSFBNͰϫʔυΧ΢ϯτ
    w NBQSFEVDFΛߦ͏4USFBNॲཧɻ
    val data: Seq[String] = Seq("apple banana", "apple banana", "orange apple mango",
    "kiwi papaya orange", "mango orange muscat apple").flatMap(_.split("\\s"))


    val source: Source[String, NotUsed] = Source(data.toVector)


    val flow: Flow[String, (String, Int), NotUsed] = Flow[String]

    .groupBy(data.distinct.size, identity)

    .map(_ -> 1)

    .reduce((l, r) => (l._1, l._2 + r._2))

    .mergeSubstreams


    val sink: Sink[(String, Int), Future[Map[String, Int]]] =
    Sink.fold[Map[String, Int], (String, Int)](Map.empty){ (result, e) => result + e }


    val g: RunnableGraph[Future[Map[String, Int]]] =
    source.via(flow).toMat(sink)(Keep.right)


    val f: Future[Map[String, Int]] = g.run()

    val v: Map[String, Int] = Await.result(f, Duration.Inf)


    println(v)
    Map(banana -> 2, muscat -> 1, orange -> 3, mango -> 2, apple -> 4, kiwi -> 1, papaya -> 1)

    View full-size slide

  52. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 52
    3FBE.PEFM6QEBUFS
    w BLLBQFSTJTUFODFRVFSZ
    Λ࢖ͬͯϦʔυϞσϧΛ
    ߏங͢ΔඇಉظετϦʔ
    ϜॲཧΛ࣮૷͢Δɻ
    w ॻ͖ࠐΈετϨʔδʹ
    ߹Θ࣮ͤͨ૷͕ඞཁʹ
    ͳΔͷͰɺ࣮૷͕೉͠
    ͍ɻ ͨͱ͑͹ɺ.Z42-
    ͰϚελʔҰ୆ͩͱ෼
    ࢄॻ͖ࠐΈͯ͠΋͋·Γ
    ҙຯ͕ͳ͍ɻଞʹ΋ί
    ωΫγϣϯϓʔϧͷ໰୊
    ΋͋ΔɻNBQ"TZODͰଟ
    ॏ౓Λઃఆ͢ΔͱΑ
    ͍ɻ

    class ReadModelUpdater(implicit val system: ActorSystem) {

    private val readJournal = PersistenceQuery(system).readJournalFor[LeveldbReadJournal]
    (LeveldbReadJournal.Identifier)

    private val persistenceIdsSource = readJournal.allPersistenceIds()

    private val getPersistenceWithModelNameFlow = Flow[String].map { persistenceId =>

    val modelName = persistenceId.split("-").head

    PersistenceWithModelName(persistenceId, modelName)

    }

    val getUserLatestVersionFlow = Flow[PersistenceWithModelName].map {

    case PersistenceWithModelName(pid, m) if m == UserAggregate.typeName =>

    PersistenceWithVersion(pid,
    DB.readOnly(InfraUser.getLatestVersion(_)).getOrElse(0L))

    }
    private val getEventsFlow = Flow[PersistenceWithVersion].flatMapConcat {

    case PersistenceWithVersion(pid, version) =>

    readJournal.eventsByPersistenceId(pid, version + 1, Long.MaxValue)

    }

    val updateUserEventFlow = Flow[EventEnvelope].map {

    case v@EventEnvelope(_, _, sequenceNr, UserInitialized(_, id, name, datetime)) =>

    InfraUser.createWithAttributes('id -> InfraUserId(id.value.toString),'deleted ->
    false,'name -> name,'createAt -> datetime,'updateAt -> datetime,'version -> 1)

    case v@EventEnvelope(_, _, sequenceNr, UserNameUpdated(_, id, name, datetime)) =>

    val updated = InfraUser.updateByIdAndVersion(InfraUserId(id.value.toString),
    sequenceNr).withAttributes('name -> name,'updateAt -> datetime,'version -> sequenceNr)

    if (updated == 0) throw new Exception(s"cannot be updated: $id")

    case v@EventEnvelope(_, _, sequenceNr, UserDestroyed(_, id, datetime)) =>

    val updated = InfraUser.updateByIdAndVersion(InfraUserId(id.value.toString),
    sequenceNr).withAttributes('deleted -> true,'updateAt -> datetime,'version ->
    sequenceNr)

    if (updated == 0) throw new Exception(s"cannot be updated: $id")

    }

    def start(): Future[Unit] = persistenceIdsSource

    .via(getPersistenceWithModelNameFlow)

    .via(getUserLatestVersionFlow)

    .via(getEventsFlow)

    .via(updateUserEventFlow)

    .toMat(Sink.last)(Keep.right)

    .run()


    }

    View full-size slide

  53. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 53
    ௥Ճ͢Δґଘؔ܎
    wBLLBIUUQ
    wBLLBIUUQͷ໨త͸ɺ"DUPSΛ)551Λհͯ͠΢Σ
    ϒʹެ։͢Δ͜ͱͱɺΫϥΠΞϯτͱͯ͠)551
    αʔϏεΛফඅ͢Δ͜ͱΛՄೳʹ͢Δ͜ͱɻ
    w)551ϑϨʔϜϫʔΫͰ͸͋Γ·ͤΜɻ΢Σϒαʔ
    ϏεͱΫϥΠΞϯτ͕ର࿩͢ΔͨΊͷ"DUPSϕʔ
    εͷπʔϧΩοτͰ͢ɻ

    View full-size slide

  54. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 54
    ΞϓϦέʔγϣϯͷ࣮૷
    w BLLBIUUQΛ࢖ͬͨΞϓ
    Ϧέʔγϣϯͷ࣮૷ɻ
    w ίϯτϩʔϥͷ࣮૷͸
    BLLBTUSFBNͰ࣮૷ɻ
    val userRoute: Route = path("users") {

    post {

    entity(as[CreateUserJson]) { createUserJson =>

    val result = Source.single(createUserJson)

    .via(userConverter.convertToCreateUser)

    .via(userUseCase.createUser)

    .via(userConverter.convertFromUserCreated)

    .toMat(Sink.head)(Keep.right)

    .run()

    onSuccess(result) { userCreatedJson =>

    complete(userCreatedJson)

    }

    }

    }

    }
    Http().bindAndHandle(

    handler = logRequestResult("log")(userRoute),

    interface = httpInterface,

    port = httpPort

    )

    View full-size slide

  55. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 55
    $POWFSUFSͷ࣮૷
    w Ϣʔεέʔεʹೖྗ͢
    ΔϑϩʔΛఆٛɻ
    w PG+TPO͸+40/ϞσϧΛ
    ΞϓϦέʔγϣϯϞσ
    ϧʹίϯόʔδϣϯ͢
    Δϑϩʔɻ
    trait UserConverter[A] {


    val convertToCreateUser: Flow[A, CreateUser, NotUsed]


    }


    object UserConverter {


    def ofJson: UserConverter[CreateUserJson] = 

    UserConverterOfJson


    private[flow] object UserInputFlowsOfJson
    extends UserConverter[CreateUserJson] {


    override val convertToCreateUser = {

    Flow[CreateUserJson].map { json =>

    CreateUser(json.name)

    }

    }


    }


    }

    View full-size slide

  56. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 56
    6TF$BTFͷ࣮૷
    w Ϣʔεέʔε༻ͷϑϩʔ
    ͷ࣮૷ɻ
    w PG"DUPS͸ू໿ΞΫλʔ
    ʹॻ͖ࠐΈ༻ϝοηʔ
    δΛૹ৴͢Δ ໭Γ஋
    ͸ΠϕϯτͰ͸ͳ͘ɺ
    Ϩεϙϯε͕औಘͰ͖
    Ε͹Α͍
    ɻ
    trait UserUseCase {

    protected val convertToCreateUserDomainCommand:
    Flow[app.CreateUser, domain.CreateUser, NotUsed] =

    Flow[app.CreateUser].map { cmd =>

    domain.CreateUser(CommandRequestId(UUID.randomUUID()),

    UserId(UUID.randomUUID()),

    cmd.name)

    }

    val createUser: Flow[app.CreateUser, app.UserCreated, NotUsed]
    }


    object UserUseCase {

    def ofActor(userActorRef: ActorRef)
    (implicit timeout: Timeout): UserUseCase =
    new UserUseCaseOfActor(userActorRef)

    private[usecase]
    case class UserUseCaseOfActor(userActorRef: ActorRef)
    (implicit timeout: Timeout) extends UserUseCase {

    override val createUser = {

    convertToCreateUserDomainCommand.mapAsync(1) { cmd =>

    (userActorRef ? cmd).mapTo[domain.CreateUserSucceeded]

    }.map { response =>

    app.UserCreated(response.aggregateId)

    }

    }


    }

    View full-size slide

  57. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 57
    $POWFSUFSͷ࣮૷
    w Ϣʔεέʔεͷ໭Γ
    ஋Λग़ྗ͢Δϑϩʔ
    Λఆٛɻ
    w PG+TPO͸ΞϓϦέʔ
    γϣϯϞσϧ͔Β
    +40/Ϟσϧʹग़ྗ͢
    Δɻ

    trait UserConverter[A] {


    val convertFromUserCreated: Flow[UserCreated, A, NotUsed]


    }


    object UserConverter {


    def ofJson: UserConverter[UserCreatedJson] =
    UserConverterOfJson


    private[flow] case object UserConverterOfJson
    extends UserConverter[UserCreatedJson] {


    override val convertFromUserCreated =

    Flow[UserCreated].map { model =>

    UserCreatedJson(JsonUserId(model.userId.value))

    }

    }


    }

    View full-size slide

  58. ࠷৽DDDΞʔΩςΫνϟͱAkkaͰͷ࣮૷ώϯτʹ͍ͭͯ
    2016/03/26 © ChatWork All rights reserved. 58
    ·ͱΊ
    w %%%$234
    w ઃܭ͕γϯϓϧʹͳΔɻϦʔυܥͷϢʔεέʔεΛ૝ఆͨ͠υϝ
    ΠϯϞσϦϯά͔Βղ์͞ΕΔɻ
    w %%%$234&4
    w $234Λ͞Βʹվྑ͢ΔɻυϝΠϯϞσϧ͸σʔλͱ͍͏ΑΓग़
    དྷࣄΠϕϯτͷू߹Ͱ͋Δͱ͍͏ࢹ఺͕ಘΒΕΔɻ͢΂͕ͯΠ
    ϕϯτͰۦಈ͢Δੈքɻ"LLB͸ͦΕΛࢧ͑Δج൫తͳπʔϧΩο
    τɻ࣮ͨͩ૷্ͷ՝୊΋ଟ͍ɻ-BHPNͷίʔυΛಡΜͰΈΔͱ
    Α͍͔΋
    w "LLBʹΑΔ࣮૷
    w $234&4ͷͨΊʹ"LLB͕͋Δͱݴͬͯ΋աݴͰ͸ͳ͍ɻ͔͠
    ͠ɺֶश͢΂͖ίϯϙʔωϯτ͕ଟ͍ɻ·ͩεςʔϒϧͰͳ͍΋
    ͷ΋ଟ͍ɻٯʹ͍͏ͱࠓͷ͏ͪʹ४උ͓͚ͯ͠͹͍͍ʂ

    View full-size slide