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

モデル駆動型開発によるビジネスをソフトウェアに落し込む1つのやり方

 モデル駆動型開発によるビジネスをソフトウェアに落し込む1つのやり方

BPStudy#141〜DDD(Domain Driven Design)実践の現場
https://bpstudy.connpass.com/event/128410/
にてお話した際のプレゼン資料です。

最近、ドメイン駆動設計を活用して開発を実施されている所が増えているのではないかと思いますが、まだまだ事例等の情報が少ないと思います。

株式会社アクティア は、「モデル駆動開発でソフトウェア開発を効率化しよう!」 をキーワードにモデリングを活用したソフトウェア開発を実現させようという企業です。

我々は、モデル駆動開発を進めていく中で、ドメイン駆動設計や増田さんに出会いました。

現在、我々の開発方法の中にはドメイン駆動設計の考え方が組み込まれ、活用しながら日々ソフトウェア開発を実施していま す。

ドメイン駆動設計の実践例として、我々のやり方が1つの参考になればと思いますので、ご紹介いたします。

#モデル駆動型開発 #ドメイン駆動設計 #DDD #ユビキタス言語 #ドメインモデル貧血症

KentaroTakasaki

May 29, 2019
Tweet

More Decks by KentaroTakasaki

Other Decks in Programming

Transcript

  1. A c t i e r A c t i

    e r גࣜձࣾΞΫςΟΞ ॴࡏ஍ɹɹɹɿɹ౦ژ౎தԝ۠೔ຊڮງཹொ ઃཱɹɹɹɹɿɹ೥ɹ݄  ϗʔϜϖʔδɿɹIUUQXXXBDUJFSDPKQ ैۀһ਺ɹɹɿɹ໊ʢऔక໾ؚΉʣ ΍ͬͯΔ͜ͱɿɹϞσϧۦಈܕ։ൃ΍%%%Λ׆͔ͨ͠ ιϑτ΢ΣΞ։ൃ
  2. A c t i e r A c t i

    e r גࣜձࣾΞΫςΟΞ +BWB✕%%% ͷೱ͍؀ڥ ࣾһืूதʂ
  3. ʲϞσϦϯάʳ ސ٬ ސ٬*%
 ސ٬໊ ஫จ ஫จ*%
 ஫จ೔ ܾࡋ ΫϨδοτΧʔυ ݱۚ

    ͋Δ෺ମ΍ࣄ৅ʹ͍ͭͯண໨͍ͯ͠Δಛ௃΍ɺಉछͷෳ਺ͷର৅ʹڞ௨͢Δੑ࣭Λநग़͠ɺ ࠣ຤ͳࡉ෦Λ؆ུԽͨ͠ந৅తͳ໛ܕʢϞσϧʣΛ࡞੒͢Δ͜ͱɹʢF8PSETΑΓʣ Ϋϥεਤ ϞσϧΛ࡞Δ͜ͱͰ෼͔Γ΍͘͢͢Δ
  4. Ϗδωε
 ཁٻ ʢυϝΠϯʣ ʲϏδωεͱιϑτ΢ΣΞͷဃ཭ʳ ιϑτ΢ΣΞ ʢιʔείʔυʣ Ϣʔβͷཁٻͱίʔυͷ ڑ཭͸ඇৗʹԕ͍ɻ ΤϯυϢʔβʹίʔυ͸ཧղͰ͖ͳ͍ Ϗδωε


    ཁٻ ʢυϝΠϯʣ ιϑτ΢ΣΞ ʢιʔείʔυʣ தؒ ੒Ռ෺ தؒ੒Ռ෺͕͋ΔͱϢʔβ΋ཧղ͠΍͘͢ ࠷ऴతͳΠϝʔδΛڞ༗͠΍͍͢
  5. Ϟσϧۦಈܕ։ൃ .%" ͱ͸ʁ ɾϞσϦϯάݴޠΛ୯ͳΔσβΠϯͰ͸ͳ͘ɺϓϩάϥϛϯάݴޠʹ͍ۙܗͰ࢖͏ ɾϞσϧத৺Ͱ։ൃ͢Δ͜ͱͰɺੜ࢈ੑ޲্ɺ඼࣭޲্ɺઃܭͷ࠶ར༻ͱ͍ͬͨ͜ͱʹ
 ɹΞϓϩʔν͢Δख๏ $*.
 $PNQVUBUJPO*OEFQFOEFOU.PEFM
 1*.
 1MBUGPSN*OEFQFOEFOU.PEFM

    14.
 1MBUGPSN4QFDJpD.PEFM
 14.
 1MBUGPSN4QFDJpD.PEFM
 $0%& $0%& $*. ίϯϐϡʔλγεςϜ͔Βಠཱͨ͠Ϟσϧɻ ϏδωεϞσϧ΍ސ٬ཁٻͳͲͷϞσϧɻ 1*. ϓϥοτϑΥʔϜಠཱϞσϧɻ $*.ΛγεςϜͱ࣮ͯ͠ݱ͢ΔͨΊʹ෼ੳ΍ઃܭ ΛߦͬͨϞσϧɻ 14. ϓϥοτϑΥʔϜݻ༗Ϟσϧɻ 1*.ʹϓϩάϥϛϯάݴޠɺϛυϧ΢ΣΞͳͲʹ ಛԽͨ͠ઃܭɺ࣮૷ʹ͍ۙཁૉΛೖΕͨϞσϧɻ $0%& ࣮ߦ͞ΕΔϓϩάϥϛϯάɻ package jp.co.actier.mars.concept.m2m.d3.acceleo.lib; import org.eclipse.emf.common.util.BasicEList;
 import org.eclipse.emf.common.util.EList; public class Uml2Util { public EList<Property> attributeAll(Class clazz) { EList<Property> attributes = new BasicEList<Property>(); if (clazz.getSuperClasses() != null) for (Class parent : clazz.getSuperClasses()) attributes.addAll(attributeAll(parent)); attributes.addAll(clazz.getOwnedAttributes()); return attributes; } } Ϟ
 σ
 ϧ
 ʹ
 ू
 த ࣗ ಈ Խ ʹ Α Δ ิ ॿ ΞΧ΢ϯτ σΟϥʔ 受付୲౰ 企ը୲౰
  6. ʲݴޠͷྺ࢙͸ந৅౓্͕͕͍ͬͯΔʳ ࣌ؒͱϓϥοτϑΥʔϜ ந
 ৅
 ౓ Ϛγϯޠ
 ͱͷූ߸ ΞηϯϒϦݴޠ
 Ξηϯϒϥ ୈੈ୅ݴޠ


    $0#0-
 $
 ΦϒδΣΫτࢦ޲
 ୈੈ୅ݴޠ
 +BWBɹ$
 3VCZɹ1ZUIPO
 Ϟσϧۦಈ։ൃ
 ϝΠϯϑϨʔϜͷ࣌୅ 1$ͷ࣌୅ Πϯλʔωοτͷ࣌୅ Ϋϥ΢υͷ࣌୅ ɾΑΓίϯϐϡʔλ͔Βਓؒܥ΁ ɾϓϥοτϑΥʔϜ͕ෳࡶʹͳΔதɺϓϥοτϑΥʔϜͱͷ෼཭͕ඞཁ
  7. ʲ"*ͷϒʔϜ΋Կճ͔ʳ       ୈҰ࣍"*ϒʔϜ ʢਪ࿦ɾ୳ࡧʣ ୈೋ࣍"*ϒʔϜ

    ʢ஌ࣝ޻ֶʣ ୈࡾ࣍"*ϒʔϜ ʢػցֶशɾදݱֶशʣ &-*;" %FFQ#MVF "MQIB(P 4JSJ %FFQ.JOE 8BUTPO σΟʔϓϥʔχϯά ػցֶश Ϗοάσʔλ ΢Σϒ ΤΩεύʔτγεςϜ .ZDJO %FOESBM ϒʔϜͷͨͼʹਐԽ͍ͯ͠Δ
  8. υϝΠϯۦಈઃܭ΍ΦϒδΣΫτࢦ޲͕ ࠶ͼ΍͖͍ͬͯͯΔʁ        6.-

    ϒʔϜ υϝΠϯۦಈઃܭϒʔϜ དྷͯΔͰ͠ΐʂʂ #14UVEZ ʙ%%% %PNBJO%SJWFO%FTJHO  ɹ࣮ફͷݱ৔ ໊௒͑Ͱ ձ৔มߋʂ
  9. ʲΞΫςΟΞ೥දʳ       ˒ ೥݄ ΞΫςΟΞ஀ੜ

    A c t i e r A c t i e r ˒ ೥݄ ߴ࡚ࢀը ઌۦऀͱͷܨ͕ΓΛ ͔͚Δ͘ΜʹͯυϝΠϯۦಈઃܭ࣮ݧ %%%"MMJBODFओ࠵ ˒ ೥݄ࠒ ૿ా͞Μͱܨ͕Δ   NBSTΛ࢖͍֤छҊ݅ͰɺεϞʔϧΦϒδΣΫτઃܭ΍υϝΠϯۦಈઃܭͱϞσϧۦಈΛ׆༻ Ϟσϧϕʔειϑτ΢ΣΞ։ൃίϛϡχςΟओ࠵
  10. ʲ౰ॳऔ૊ΜͰ͍ͨϞσϧۦಈ։ൃʳ ސ٬ ސ٬*%
 ސ٬໊ ஫จ ஫จ*%
 ஫จ೔ ܾࡋ ΫϨδοτΧʔυ ݱۚ

    6.-Ϋϥεਤ ࣗಈੜ੒ ιʔείʔυ ɾάϥϑΟΧϧͩͱύοͱݟͰ෼͔Γ΍͍͕͢ɺࠩ෼؅ཧ͕೉͍͠ ɾ੩తߏ଄͸දݱ͠΍͍͕͢ɺಈతߏ଄ʢৼΔ෣͍ʣ͕දݱ͠ʹ͍͘
  11. ʲ෼ੳɾઃܭͷτϨʔχϯάΛఏڙʳ $        

     $   %! $    $    $  %!  $ "#  A c t i e r A c t i e r ૿ా͞Μͱڞʹʲ෼ੳɾઃܭʳΛ௥ٻ
  12. ʲ%4-Πϝʔδʳ /** * ঎඼(ࢴ) */ package jp.xx.xxx.xxx.domain.model.item.paper /** ঎඼ʢࢴʣ */

    listType PaperItems<PaperItem> /** ࢴ঎඼ϦϏδϣϯ */ longType Revision /** ࢴ঎඼ */ object PaperItem { ItemCode itemCode ChartType chartType {required} ItemDescription itemDescription Revision revision Publication publication SalesUnitPrice salesUnitPrice StockControl stockControl def ItemCode itemCode(){ return itemCode } def SalesUnitPrice salesUnitPrice() { salesUnitPrice } %4- ύοέʔδߏ੒΍ιʔε ϘΠϥʔςϯϓϨʔτͳ ίʔυΛҙࣝͤͣʹࡁΉ ʢίϐϖΛແ͘͠ਓతϛεΛݮΒ͢ʣ 
 Α͋͘Δࣗಈੜ੒ܥͱͷࠩผԽͱͯ͠ Մಡੑͷ͋Διʔεʹ߆Δ ϞσϦϯάͱͯ͠ ਓ͕஫ྗ͢΂͖ͱ͜Ζ͚ͩ ࣗಈੜ੒
  13. ʲݴ༿ͷဃ཭ʳ Ϗδωε υϝΠϯ ιϑτ΢ΣΞ ʢιʔείʔυʣ Ϟσϧ υϝΠϯΤΩεύʔτ͸ɺ ιϑτ΢ΣΞ։ൃʹ͓͚Δٕज़తͳઐ໳༻ޠͷ͜ͱ͸෦෼తʹ͔͠ཧղͤͣɺ ୅ΘΓʹࣗ෼ͷಘҙ෼໺ͷઐ໳༻ޠΛ࢖༻͢Δ υϝΠϯ

    ΤΩεύʔτ ։ൃऀ͸ɺ γεςϜΛཧղͯٞ͠࿦͢Δࡍʹɺػೳʹؔ͢Δઆ໌తͳ༻ޠΛ༻͍Δ͔΋͠ Εͳ͍͕ɺ͜͏ͨ͠༻ޠ͸υϝΠϯΤΩεύʔτͷݴޠ͕͍࣋ͬͯͨҙຯΛܽ ͍ͯ͠·͍ͬͯΔɻ͋Δ͍͸ɺ։ൃऀʹΑΔந৅Խ͸ɺࣗ෼ͨͪͷઃܭΛࢧ͑ ͯ͸͍ͯ΋ɺυϝΠϯΤΩεύʔτʹ͸ཧղ͞Εͳ͍͔΋͠Εͳ͍ ։ൃऀ ݴ༿ͷؒʹ͸ʲஅઈʳ͕͋Δ
  14. ʲϢϏΩλεݴޠʳ Ϗδωε υϝΠϯ ιϑτ΢ΣΞ ʢιʔείʔυʣ Ϟσϧ υϝΠϯ ΤΩεύʔτ ։ൃऀ ϞσϧΛݴޠͷࠎ֨ͱ͢Δ

    ͢΂ͯͷίϛϡχέʔγϣϯͱίʔυʹɺͦͷݴޠΛ ݫ֨ʹ༻͍Δ 
 ݴޠʹ໰୊͕͋Ε͹୅ΘΓͱͳΔݴ༿Λ୳͠ɺϞσϧ Λ৽ͯ͘͠͠ίʔυΛϦϑΝΫλϦϯά͢Δ ݴޠʢݴ༿ʣ Ұ؏ͨ͠ݴޠʢݴ༿ʣͰڞ༗
  15. "7$4 ച্ ిࢠւਤ ݟੵ ೲೖઌ &/$ ɾɾɾ "%1 ʲݴ༿͕ͨ͘͞Μʳ ધओ

    ધ۩঎ ધഫ؅ཧձࣾ 6,)0 $IBSU$P ࢴւਤ ॻ੶ ਫ࿏௨ใ
  16. ʲNBSTͰ஋ΦϒδΣΫτʳ longType integerType decimalType stringType enum enumType stringType CustomerId ܕ

    αϯϓϧ integerType SignedAmount { max: 100 min: -100 } enum Lang(String label) { JA("Japanese") EN('English') } enumType LangType<Lang> { def boolean isJapanese() { this.value == Lang.JA } def boolean isEnglish() { this.value == Lang.EN } } listType ϑΝʔετΫϥείϨΫγϣϯΛؚΊ ஋ΦϒδΣΫτΛύλʔϯԽ object dateType dateType QuotationDate { format: 'yyyy-MM-dd' def static QuotationDate ofThreeMonthAgo(){ new QuotationDate(LocalDate.now.minusMonths(3)) } }
  17. /** ݴޠʢͨͩ͠ɺʮݴޠʯ͸௨ৗͷݴ༿ͱ͍͏ҙຯͰ͸ແ͍ͷͰ஫ҙɻࠃ΍঎඼෼ྨΛ૯শͨ͠΋ͷʣ */ package jp.xx.xxx.xxx.domain.model.classification.language /** ݴޠID༻ͷྻڍࢠ */ enum LanguageIdValue(঎඼छྨࣝผࢠ

    identifier, String description) { JP(঎඼छྨࣝผࢠ.ࢴ, "೔ຊ൛ͷࢴւਤɾॻ੶ʢൃߦݩɿ೔ຊਫ࿏ڠձ/ւ্อ҆ி/ւ্อ҆ڠձʣ") JB(঎඼छྨࣝผࢠ.ࢴ, "೔ຊ൛ͷࢴւਤɾॻ੶ʢൃߦݩɿJPҎ֎ʣ") US(঎඼छྨࣝผࢠ.ࢴ, "ถࠃ൛ͷࢴւਤɾॻ੶") IM(঎඼छྨࣝผࢠ.ࢴ, "IMOͷࢴւਤɾॻ੶ɾిࢠ") KR(঎඼छྨࣝผࢠ.ࢴ, "ؖࠃ൛ͷࢴւਤɾॻ੶") OT(঎඼छྨࣝผࢠ.ࢴ, "ͦͷଞͷࢴւਤɾॻ੶") CM(঎඼छྨࣝผࢠ.σδλϧ, “C-MAPిࢠւਤ") ENC(঎඼छྨࣝผࢠ.σδλϧ, "೔ຊ൛ిࢠւਤ") AVCS(঎඼छྨࣝผࢠ.σδλϧ, "UKHOిࢠւਤ") ADP(঎඼छྨࣝผࢠ.σδλϧ, "UKHOిࢠॻ੶") eNP(঎඼छྨࣝผࢠ.σδλϧ, "UKHOిࢠॻ੶") CC(঎඼छྨࣝผࢠ.σδλϧ, "ChartCo঎඼") def boolean isPaper() { this.identifier == ঎඼छྨࣝผࢠ.ࢴ } def boolean isDigital() { this.identifier == ঎඼छྨࣝผࢠ.σδλϧ } def boolean isAvcs() { this == AVCS } def boolean isAdp() { this == ADP } def boolean isEnp() { this == eNP } } /** ݴޠID */ enumType LanguageId<LanguageIdValue> { def boolean isPaper() { value.isPaper() } def boolean isDigital() { value.isDigital() } def boolean isAvcs() { if (value === null) return false value.isAvcs } def boolean isAdp() { if (value === null) return false value.isAdp } def String name() { value.name } } listType LanguageIds<LanguageId> { def static LanguageIds values() { var list = Arrays.asList(LanguageIdValue.values) var languageIds = list.map[id | new LanguageId(id)] new LanguageIds(languageIds) } def LanguageIds paper() { if (empty) return new LanguageIds var newList = list.filter[isPaper].toList new LanguageIds(newList) } def LanguageIds digital() { if (empty) return new LanguageIds var newList = list.filter[isDigital].toList new LanguageIds(newList) } ʲ࣮ྫɿݴޠ*%ΛNBSTͰදݱʳ Ϗδωε υϝΠϯ ιϑτ΢ΣΞ ʢιʔείʔυʣ Ϟσϧ υϝΠϯ ΤΩεύʔτ ։ൃऀ ݴޠ*%
  18. ʲσʔλʢςʔϒϧઃܭʣ͔Β΍ͬͯ͘Δͱ
 ɹσʔλͷثʹͳΓ͕ͪʳ ϓϨθϯςʔγϣϯ૚ ΞϓϦέʔγϣϯ૚ σʔλιʔε૚ υϝΠϯ૚ ঎඼*% ঎඼໊ ఆՁ ࡏݿ؅ཧ

    +1 ೔ຊۙւ  ͢Δ +1 ೔ຊԊ؛෦  ͢Δ #" ӳࠃۙւ  ͠ͳ͍ #" ӳࠃԊ؛෦  ͠ͳ͍ ঎඼ςʔϒϧ طଘϓϩδΣΫτ͔Βͷ ϦϓϨΠεͩͱ ಛʹةͳ͍
  19. ʲෳࡶ͞ɾ೉͠͞͸Ϗδωεϧʔϧʹ͋Δʳ         

      ۀ຿ΞϓϦέʔγϣϯ։ൃͷෳࡶ͞ɾ೉͠͞͸ɺϏδωεϧʔϧʹ͋Δɻ ϏδωεՁ஋Λ࣮ݱ͢ΔͨΊʹϛογϣϯ΍ઓུɺ࣮ߦܭըΛܦͯઓज़͔Β
 ࣮ߦ΁ͱམͱ͠ࠐΜͰ͍͘தͰɺߦಈํ਑΍൑அج४ͱͳΔϧʔϧ͕
 ϏδωεϙϦγʔ΍Ϗδωεϧʔϧͱͯ͠Լࢧ͍͑ͯ͠·͢ɻ ͦͷϧʔϧͷൃݟɾ෼ੳɾ࣮૷ʹ࣠Λஔ͘͜ͱͰΞϓϦέʔγϣϯ੔ཧ͕
 ༰қʹͳΓ·͢ɻ ϏδωεϧʔϧΛൃݟɾ෼ੳ͠ɺ υϝΠϯϞσϧͱ࣮ͯ͠૷͍ͯ͘͠
  20. ʲ࣮ྫɿ਺ྔ͔ఆՁ͕ϧʔϧΛ࣋ͭʳ αϯϓϧ /** ݟੵ໌ࡉ */ object PaperQuotationDetail impl Detail {

    SalesPaperItem item { no-validation } Quantity quantity SalesUnitPrice salesUnitPrice DiscountRate discountRate ɹɹɹɾɾɾ def DetailAmount amount() { return quantity.calculateAmount(salesUnitPrice, voucherForeignExchange.voucherCurrency) } } /** ໌ࡉ਺ྔ */ integerType Quantity { assert("") lessThanMaxQuantity { value < 10000 } def boolean isMinus(){ if(value < 0) return true return false } ɹɹɹɾɾɾ def DetailAmount calculateAmount(SalesUnitPrice salesUnitPrice, SalesCurrency salesCurrency) { new DetailAmount(new BigDecimal(value).multiply(salesUnitPrice.decimalValue())) } } ϏδωεϧʔϧΛ஌͍ͬͯΔͷ͸ ਺ྔͳͷ͔ʁఆՁͳͷ͔ʁ ໌ࡉֹۚ ਺ྔ΍ఆՁ͕໌ࡉֹۚʹ ґଘ͢Δͷ͸Կ͔ඍົ
  21. ʲ࣮ྫɿ໌ࡉֹ͕ۚϧʔϧΛ࣋ͭʳ ݟੵ໌ࡉϦετ ݟੵ ݟੵ໌ࡉ ໌ࡉֹۚ ໌ࡉֹۚ ఆՁ ਺ྔ ✕ SFUVSO

    ໌ࡉֹۚͷίϯετϥΫλͰ ఆՁͱ਺ྔΛ౉ͯ͠ ஋ͷऔಘ࣌ʹܭࢉ ໌ࡉֹۚͷதʹϏδωεϧʔϧΛ
 ؅ཧ͢Δߏ଄ͱͳͬͨ ఆՁͱ਺ྔΛֻ͚Δࡍʹ ͦΕͧΕ͔ΒܭࢉՄೳͳ஋Λ औಘ͢Δඞཁ͕ग़͖ͯͨ
  22. ʲ࣮ྫɿ໌ࡉֹ͕ۚϧʔϧΛ࣋ͭʳ /** ݟੵ໌ࡉ */ object PaperQuotationDetail impl Detail { SalesPaperItem

    item { no-validation } Quantity quantity SalesUnitPrice salesUnitPrice DiscountRate discountRate ɹɹɹɾɾɾ def DetailAmount amount() { return new DetailAmount(quantity, salesUnitPrice, voucherForeignExchange.voucherCurrency) } } /** ݟੵ໌ࡉֹۚ */ object DetailAmount impl ApplicableAmount, ExchangeBaseAmount { Quantity quantity SalesUnitPrice salesUnitPrice SalesCurrency voucherCurrency init(Quantity quantity, SalesUnitPrice salesUnitPrice, SalesCurrency voucherCurrency) { this.quantity = quantity this.salesUnitPrice = salesUnitPrice this.voucherCurrency = voucherCurrency } ɹɹɹɾɾɾ def BigDecimal decimalValue() { if (salesUnitPrice.empty) return BigDecimal.ZERO if (quantity.empty) return salesUnitPrice.decimalValue return salesUnitPrice.decimalValue().multiply(new BigDecimal(quantity.intValue())) } } ໌ࡉֹۚͷதʹϏδωεϧʔϧΛ
 ؅ཧ͢Δߏ଄ͱͳͬͨ ໌ࡉֹۚͷίϯετϥΫλͰ ఆՁͱ਺ྔΛ౉ͯ͠ ஋ͷऔಘ࣌ʹܭࢉ ఆՁͱ਺ྔΛֻ͚Δࡍʹ ͦΕͧΕ͔ΒܭࢉՄೳͳ஋Λ औಘ͢Δඞཁ͕ग़͖ͯͨ ໌ࡉֹۚ αϯϓϧ
  23. A c t i e r A c t i

    e r גࣜձࣾΞΫςΟΞ +BWB✕%%% ͷೱ͍؀ڥ ࣾһืूதʂ େࣄͳͷͰɺ΋͏Ұ౓
  24. ׬