$30 off During Our Annual Pro Sale. View Details »

PofEAAで考えるSaaSバックエンドの作り方

男爵
October 17, 2020

 PofEAAで考えるSaaSバックエンドの作り方

ScalaMatsuri2020の登壇資料です。

男爵

October 17, 2020
Tweet

More Decks by 男爵

Other Decks in Programming

Transcript

  1. Making of SaaS Backend
    guided by PofEAA
    @dnskimox
    PofEAAで考える
    SaaSバックエンドの作り方

    View Slide

  2. Who am I
    ● Tanga Kenichi
    ● @dnskimox
    ● Backend Developer
    ● SaaS for contract management
    2
    自己紹介

    View Slide

  3. PofEAA
    The book about powerful
    design patterns for
    “Enterprise Application”
    written by Martin Fowler
    at 2002.
    3
    邦題:エンタープライズアプリケーションアーキテクチャ
    パターン

    View Slide

  4. What is the
    “Enterprise application”?
    Enterprise application does:
    ● store vary data
    ● manage huge data
    ● accept many requests at a time
    ● collaborate with other Enterprise
    applications
    4
    エンタープライズアプリケーションとは

    View Slide

  5. Enterprise Application ≒ Web Service
    5
    Especially a web service requiring massive development
    ほぼWEBサービスのこと

    View Slide

  6. A web service of this session’s subject is...
    このセッションで取り上げる架空のWEBサービス
    *this service is fictitious
    MakeInvoice

    View Slide

  7. Contents
    7
    About MakeInvoice
    The way we have managed such a
    large, complex application
    Architecture in MakeInvoice
    Service description about
    MakeInvoice, invoice creation and
    management SaaS
    Design patterns that will be
    worthwhile to adopt to MakeInvoice
    Adoptable Patterns
    1
    2
    3
    目次

    View Slide

  8. 1. About MakeInvoice
    Service description about
    MakeInvoice, invoice
    creation and management
    SaaS
    MakeInvoiceについて

    View Slide

  9. CloudSign
    Salesforce
    Dashboard(SPA)
    Client Service
    MakeInvoice

    View Slide

  10. Simple Domain Model
    素朴なドメインモデル

    View Slide

  11. Slightly Complex Domain Model
    やや複雑なドメインモデル

    View Slide

  12. More Complex Domain Model
    さらに複雑なドメインモデル

    View Slide

  13. And More...
    さらにさらに

    View Slide

  14. This logic has to consider:
    ● difference between regular and discount
    item
    ● tax calculation
    ● to manipulate a template of document
    Invoice Creation
    請求書生成のビジネスロジック

    View Slide

  15. This logic has to consider:
    ● partial payment
    ● overpaying
    ● carrying over to next month
    Reconciliation
    入金消込のビジネスロジック

    View Slide

  16. This logic has to consider:
    ● resending invoice
    ● how to handling old invoice
    ● difference between total price and price
    paid partially
    Demand for Payment
    支払催促のビジネスロジック

    View Slide

  17. Combine and Split Invoice
    請求書の結合と分割のビジネスロジック

    View Slide

  18. Large domain model
    structure
    Complex business
    logic
    Requests from
    dashboard, from
    Salesforce, from
    other services using
    MakeInvoice
    Some other SaaS to
    have to connect
    18
    Characteristics 
    of MakeInvoice
    backend API
    MakeInvoiceのバックエンドAPIの特徴

    View Slide

  19. 2. Architecture in
    MakeInvoice
    The way we have managed
    such a large, complex
    application
    MakeInvoiceのアーキテクチャ

    View Slide

  20. Clean Architecture

    View Slide

  21. Domain Layer
    UseCase Layer
    Primary / Secondary Adapter
    Code for technical interest and implementation
    i.e: Http, gRPC, RDB, KVS
    Represents common use-cases using domain model
    Contains only “pure” domain concepts and logic
    Dependency
    direction
    各レイヤーの責務
    依存の方向性

    View Slide

  22. Hexagonal architecture
    This architecture aims to:
    ● receive request from
    variety of clients
    ● reuse essential application
    code
    ● write data on several data
    storages or send to other
    system
    22
    https://blog.tai2.net/hexagonal_architexture.html
    ヘキサゴナルアーキテクチャ

    View Slide

  23. Hexagonal architecture
    ● Primary Adapters are
    adapters to communicate
    with primary actor.
    ● Secondary Adapters are
    adapters to communicate
    with supporting actor (also
    known as secondary
    actor).
    23
    https://blog.tai2.net/hexagonal_architexture.html
    プライマリアダプターと主アクター
    セカンダリアダプターと支援アクター

    View Slide

  24. 3. Adoptable Patterns
    Design patterns that will
    be worthwhile to adopt to
    MakeInvoice
    適用可能なパターン

    View Slide

  25. Domain Layer
    UseCase Layer
    Primary / Secondary Adapter
    DataMapper, SingleTableInheritance,
    EmbeddedValue, SerializedLOB, DependentMapping,
    DataTransferObject, ServiceStub
    (Nothing)
    DomainModel, POJO, ValueObject, Repository
    レイヤー毎のパターン

    View Slide

  26. Representing domain model is
    simple, unless domain classes
    depend on any library or framework.
    Domain Layer
    ドメインレイヤー

    View Slide

  27. 会社、顧客、書類、請求書、請求書品目

    View Slide

  28. “We wondered why people were
    so against using regular objects
    in their systems and concluded
    that it was because simple
    objects lacked a fancy name. ”
    28
    https://martinfowler.com/bliki/POJO.html
    Plain Old Java Object (POJO)
    昔からある単なるJavaオブジェクト

    View Slide

  29. case class Invoice (
    id: DocumentId,
    companyId: CompanyId,
    customerId: CustomerId,
    items: Seq[InvoiceItem],
    billingDate: LocalDate,
    billingDueDate: LocalDate,
    status: InvoiceStatus
    ) extends Document
    abstract class Document {
    val id: DocumentId
    val companyId: CompanyId
    val customerId: CustomerId
    }

    View Slide

  30. “An object model of the domain
    that incorporates both
    behavior and data.”
    30
    Domain Model
    https://www.martinfowler.com/eaaCatalog/domainModel.html
    ドメインモデル

    View Slide

  31. case class Invoice (
    id: DocumentId,
    companyId: CompanyId,
    customerId: CustomerId,
    items: Seq[InvoiceItem],
    billingDate: LocalDate,
    dueDate: LocalDate,
    status: InvoiceStatus
    ) extends Document {
    def totalPrice: Money = ???
    def subtotal: Money = ???
    def tax: Money = ???
    def combine(other: Invoice): Invoice = ???
    def split(prices: Seq[Money]): Seq[Invoice] = ???
    }

    View Slide

  32. “A small simple object, like
    money or a date range, whose
    equality isn't based on
    identity.”
    32
    Value Object
    https://www.martinfowler.com/eaaCatalog/valueObject.html
    値オブジェクト

    View Slide

  33. case class Money(
    currency: Currency,
    value: BigDecimal
    )
    case class Currency(
    currencyCode: String
    )

    View Slide

  34. case class Money(
    currency: Currency,
    value: BigDecimal
    ) {
    def plus(other: Money): Money = ???
    def minus(other: Money): Money = ???
    def mul(factor: BigDecimal): Money = ???
    def div(factor: BigDecimal): Money = ???
    }

    View Slide

  35. case class Address(
    countryCode: CountryCode,
    zipCode: ZipCode,
    city: String,
    address1: String,
    address2: String
    ) {
    def isSame(other: Address): Boolean = ???
    }

    View Slide

  36. “Mediates between the domain
    and data mapping layers using a
    collection-like interface for
    accessing domain objects.”
    36
    https://www.martinfowler.com/eaaCatalog/repository.html
    Repository
    リポジトリ

    View Slide

  37. 書類リポジトリ

    View Slide

  38. trait DocumentRepository {
    def store[R :_trantask :_error](document: Document): Eff[R, Document]
    def resolve[R :_trantask :_error](id: DocumentId): Eff[R, Document]
    def matching[R :_trantask :_error](criteria: Criteria): Eff[R, Seq[Document]]
    }
    case class Criteria(
    companyId: Option[CompanyId],
    customerId: Option[CustomerId],
    documentType: Option[DocumentType]
    ) {
    def isSatisfiedBy(document: Document): Boolean = ???
    }

    View Slide

  39. What is EFF?
    39
    For more information, please go to Yoshimura‘s session.
    EFFについて

    View Slide

  40. Design Domain Layer
    from Domain Model
    not from database.
    40
    ドメインモデルから設計を始める

    View Slide

  41. otherwise
    41
    さもないと

    View Slide

  42. “By pulling all the behavior out
    into services, however, you
    essentially end up with
    Transaction Scripts, and thus lose
    the advantages that the domain
    model can bring. ”
    42
    https://martinfowler.com/bliki/AnemicDomainModel.html
    AnemicDomainModel
    ドメインモデル貧血症

    View Slide

  43. This layer contains business
    transaction only. So meaningful
    patterns adopted here would be
    honestly nothing.
    UseCase Layer
    ユースケースレイヤー

    View Slide

  44. class CreateDocumentUseCase @Inject() (
    documentRepository: DocumentRepository,
    documentFactory: DocumentFactory
    ) {
    def execute[R :_trantask :_error](
    documentType: DocumentType,
    ...
    ): Eff[R, Unit] =
    for {
    document <- documentFactory.create(documentType, ...)
    _ <- documentRepository.store(document)
    } yield ()
    }

    View Slide

  45. Secondary Adapter
    Main concern in this layer probably is
    mapping objects to/from relational
    database. Also clients to request to
    external service is in this layer.
    セカンダリアダプター

    View Slide

  46. “A layer of Mappers that moves
    data between objects and a
    database while keeping them
    independent of each other and
    the mapper itself.”
    46
    https://www.martinfowler.com/eaaCatalog/dataMapper.html
    Data Mapper
    データマッパー

    View Slide

  47. “An object that sets up a
    communication between two
    independent objects.”
    47
    https://www.martinfowler.com/eaaCatalog/mapper.html
    Mapper
    マッパー

    View Slide

  48. Table schema
    is free
    from domain model.
    48
    テーブルスキーマは自由

    View Slide

  49. Separation of Concerns
    49
    関心の分離

    View Slide

  50. class DocumentRepositoryImpl {
    def store[R :_trantask :_error](document: Document): Eff[R, Document]
    = ???
    def resolve[R :_trantask :_error](id: DocumentId): Eff[R, Document]
    = ???
    def matching[R :_trantask :_error](criteria: Criteria): Eff[R, Seq[Document]]
    = ???
    }

    View Slide

  51. Impedance Mismatch
    51
    Object Oriented World
    ● Abstraction
    ● Composition
    Relational DB World
    ● Set
    ● Relation
    インピーダンスミスマッチ

    View Slide

  52. Impedance Mismatch
    52
    Object Oriented World
    ● Abstraction
    ● Composition
    Relational DB World
    ● Set
    ● Relation
    抽象化を集合と関係の世界へ

    View Slide

  53. 抽象クラスの書類

    View Slide

  54. “Represents an inheritance
    hierarchy of classes as a single
    table that has columns for all the
    fields of the various classes.”
    54
    https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
    Single Table Inheritance
    シングルテーブル継承

    View Slide

  55. 書類テーブル

    View Slide

  56. 書類の種類を増やす

    View Slide

  57. 書類テーブル Ver.2

    View Slide

  58. Many nullable fields!
    58
    Nullを許容するフィールドがたくさん

    View Slide

  59. “Represents an inheritance
    hierarchy of classes with one
    table for each class.”
    59
    https://www.martinfowler.com/eaaCatalog/classTableInheritance.html
    Class Table Inheritance
    クラステーブル継承

    View Slide

  60. 書類テーブル Ver.3

    View Slide

  61. Impedance Mismatch
    61
    Object Oriented World
    ● Abstraction
    ● Composition
    Relational DB World
    ● Set
    ● Relation
    コンポジションを集合と関係の世界へ

    View Slide

  62. 顧客に含まれる名前オブジェクト

    View Slide

  63. “Maps an object into several
    fields of another object's table.”
    63
    https://www.martinfowler.com/eaaCatalog/embeddedValue.html
    Embedded Value
    組込バリュー

    View Slide

  64. 顧客テーブル

    View Slide

  65. 顧客に含まれる住所オブジェクト

    View Slide

  66. x 1?
    顧客テーブル Ver.2

    View Slide

  67. “Saves a graph of objects by
    serializing them into a single
    large object (LOB), which it
    stores in a database field.”
    67
    https://www.martinfowler.com/eaaCatalog/serializedLOB.html
    Serialized LOB
    シリアライズドLOB

    View Slide

  68. x ∞
    顧客テーブル Ver.3

    View Slide

  69. Not bad
    69
    悪くない

    View Slide

  70. But...
    70
    でも

    View Slide

  71. ● Managing scheme is
    difficult.
    ● Adding and removing
    property is
    problematic.
    ● Result of SQL is not
    human friendly.
    ● Managing scheme is
    almost unnecessary.
    ● New table is not
    needed.
    ● Table joining does
    not occur.
    ● Object count is
    unlimited.
    Using serialized field has pros and cons
    Pros Cons
    シリアライズされたフィールドの功罪

    View Slide

  72. “Has one class perform the
    database mapping for a child
    class.”
    72
    https://www.martinfowler.com/eaaCatalog/dependentMapping.html
    Dependent Mapping
    依存マッピング

    View Slide

  73. x ∞
    顧客テーブル Ver.4
    Dependent Object

    View Slide

  74. ● When owner object is
    updated, dependent
    objects are all
    deleted then
    re-inserted.
    ● When owner object is
    deleted, dependent
    objects are deleted
    cascadingly.
    ● Any object except for
    owner cannot
    associate with
    dependent object.
    ● Dependent object's
    table has foreign key
    for owner's table.
    Dependent object's characteristic
    Association/Relation Update/Delete
    依存オブジェクトの特徴

    View Slide

  75. “Removes dependence upon
    problematic services during
    testing. WSDL ”
    75
    https://www.martinfowler.com/eaaCatalog/dependentMapping.html
    Service Stub
    サービススタブ

    View Slide

  76. クラウドサインサービス

    View Slide

  77. Primary Adapter
    This layer is so-called "Presentation
    Layer". To parse request from client
    and to compose response are its
    responsibility. Also main App for
    running batch program is contained
    here.
    プライマリアダプター

    View Slide

  78. “Provides a coarse-grained
    facade on fine-grained objects to
    improve efficiency over a
    network.”
    78
    https://www.martinfowler.com/eaaCatalog/remoteFacade.html
    Remote Facade

    View Slide

  79. class DocumentController @Inject() (
    createDocumentUseCase: CreateDocumentUseCase,
    getDocumentUseCase: GetDocumentUseCase
    ) extends AkkaHttpController {
    def routes: Route =
    pathPrefix("DocumentService") {
    post {
    path("CreateDocument") { createDocument } ~
    path("GetDocument") { getDocument }
    }
    }
    private def createDocument: Route = ???
    private def getDocument: Route = ???
    }

    View Slide

  80. Split endpoints
    moderately
    coarse grained.
    80
    荒い粒度でエンドポイントを分割する

    View Slide

  81. “An object that carries data
    between processes in order to
    reduce the number of method
    calls.”
    81
    https://www.martinfowler.com/eaaCatalog/dataTransferObject.html
    Data Transfer Object
    データ変換オブジェクト

    View Slide

  82. ドキュメントアセンブラ
    ドキュメントDTO

    View Slide

  83. DTO structure
    is free
    from domain model.
    83
    DTOの構造は自由

    View Slide

  84. Separation of Concerns
    84
    関心の分離

    View Slide

  85. Domain Model
    Repository
    ValueObject
    DataMapper
    SingleTableInheritanse
    ClassTableInheritanse
    EmbededValue
    SerializedLOB
    DependentMapping
    DTO
    Remote
    Facade
    Putting It All Together
    全てのパターンをひとつに
    SPA
    DB
    UseCase
    Service Stub
    Other SaaS

    View Slide

  86. Conclusion
    86
    PofEAA put forward many
    useful patterns for complex
    web application. But , as
    Fowler writes, these patterns
    have pros and cons. So we
    have to keep thinking which
    patterns to use or not use any
    pattern.
    Use this advice to prod your
    thinking. In the end you have
    to make, and live with, the
    decisions yourself. One good
    thing is that your decisions are
    not cast forever in stone.
    - Martin Fowler
    結論

    View Slide

  87. Now, you have learned main
    concept to write code of
         backend.
    87
    もうScalebaseのバックエンドを書けるはず

    View Slide

  88. Join us!
    ご応募お待ちしております!

    View Slide

  89. Does anyone have any
    questions?
    @dnskimox
    dnskimox.hateblo.jp
    89
    Thanks!
    ご清聴ありがとうございました!

    View Slide

  90. Reference literature
    ● https://www.amazon.co.jp/dp/B008OHVDFM
    ● https://www.martinfowler.com/eaaCatalog/index.html
    ● https://bliki-ja.github.io/pofeaa/CatalogOfPofEAA_Ja/
    ● https://martinfowler.com/bliki/POJO.html
    ● https://martinfowler.com/bliki/AnemicDomainModel.html
    ● https://blog.tai2.net/hexagonal_architexture.html
    ● http://atnos-org.github.io/eff/index.html
    ● https://github.com/google/guice
    90
    参考文献

    View Slide