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

88ad4f75d7c84fcf560bb6205c52e8c1?s=47 dnskimo
October 17, 2020

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

ScalaMatsuri2020の登壇資料です。

88ad4f75d7c84fcf560bb6205c52e8c1?s=128

dnskimo

October 17, 2020
Tweet

Transcript

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

  2. Who am I • Tanga Kenichi • @dnskimox • Backend

    Developer • SaaS for contract management 2 自己紹介
  3. PofEAA The book about powerful design patterns for “Enterprise Application”

    written by Martin Fowler at 2002. 3 邦題:エンタープライズアプリケーションアーキテクチャ パターン
  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 エンタープライズアプリケーションとは
  5. Enterprise Application ≒ Web Service 5 Especially a web service

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

    service is fictitious MakeInvoice
  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 目次
  8. 1. About MakeInvoice Service description about MakeInvoice, invoice creation and

    management SaaS MakeInvoiceについて
  9. CloudSign Salesforce Dashboard(SPA) Client Service MakeInvoice

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

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

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

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

  14. This logic has to consider: • difference between regular and

    discount item • tax calculation • to manipulate a template of document Invoice Creation 請求書生成のビジネスロジック
  15. This logic has to consider: • partial payment • overpaying

    • carrying over to next month Reconciliation 入金消込のビジネスロジック
  16. This logic has to consider: • resending invoice • how

    to handling old invoice • difference between total price and price paid partially Demand for Payment 支払催促のビジネスロジック
  17. Combine and Split Invoice 請求書の結合と分割のビジネスロジック

  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の特徴
  19. 2. Architecture in MakeInvoice The way we have managed such

    a large, complex application MakeInvoiceのアーキテクチャ
  20. Clean Architecture

  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 各レイヤーの責務 依存の方向性
  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 ヘキサゴナルアーキテクチャ
  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 プライマリアダプターと主アクター セカンダリアダプターと支援アクター
  24. 3. Adoptable Patterns Design patterns that will be worthwhile to

    adopt to MakeInvoice 適用可能なパターン
  25. Domain Layer UseCase Layer Primary / Secondary Adapter DataMapper, SingleTableInheritance,

    EmbeddedValue, SerializedLOB, DependentMapping, DataTransferObject, ServiceStub (Nothing) DomainModel, POJO, ValueObject, Repository レイヤー毎のパターン
  26. Representing domain model is simple, unless domain classes depend on

    any library or framework. Domain Layer ドメインレイヤー
  27. 会社、顧客、書類、請求書、請求書品目

  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オブジェクト
  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 }
  30. “An object model of the domain that incorporates both behavior

    and data.” 30 Domain Model https://www.martinfowler.com/eaaCatalog/domainModel.html ドメインモデル
  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] = ??? }
  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 値オブジェクト
  33. case class Money( currency: Currency, value: BigDecimal ) case class

    Currency( currencyCode: String )
  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 = ??? }
  35. case class Address( countryCode: CountryCode, zipCode: ZipCode, city: String, address1:

    String, address2: String ) { def isSame(other: Address): Boolean = ??? }
  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 リポジトリ
  37. 書類リポジトリ

  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 = ??? }
  39. What is EFF? 39 For more information, please go to

    Yoshimura‘s session. EFFについて
  40. Design Domain Layer from Domain Model not from database. 40

    ドメインモデルから設計を始める
  41. otherwise 41 さもないと

  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 ドメインモデル貧血症
  43. This layer contains business transaction only. So meaningful patterns adopted

    here would be honestly nothing. UseCase Layer ユースケースレイヤー
  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 () }
  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. セカンダリアダプター
  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 データマッパー
  47. “An object that sets up a communication between two independent

    objects.” 47 https://www.martinfowler.com/eaaCatalog/mapper.html Mapper マッパー
  48. Table schema is free from domain model. 48 テーブルスキーマは自由

  49. Separation of Concerns 49 関心の分離

  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]] = ??? }
  51. Impedance Mismatch 51 Object Oriented World • Abstraction • Composition

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

    Relational DB World • Set • Relation 抽象化を集合と関係の世界へ
  53. 抽象クラスの書類

  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 シングルテーブル継承
  55. 書類テーブル

  56. 書類の種類を増やす

  57. 書類テーブル Ver.2

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

  59. “Represents an inheritance hierarchy of classes with one table for

    each class.” 59 https://www.martinfowler.com/eaaCatalog/classTableInheritance.html Class Table Inheritance クラステーブル継承
  60. 書類テーブル Ver.3

  61. Impedance Mismatch 61 Object Oriented World • Abstraction • Composition

    Relational DB World • Set • Relation コンポジションを集合と関係の世界へ
  62. 顧客に含まれる名前オブジェクト

  63. “Maps an object into several fields of another object's table.”

    63 https://www.martinfowler.com/eaaCatalog/embeddedValue.html Embedded Value 組込バリュー
  64. 顧客テーブル

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

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

  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
  68. x ∞ 顧客テーブル Ver.3

  69. Not bad 69 悪くない

  70. But... 70 でも

  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 シリアライズされたフィールドの功罪
  72. “Has one class perform the database mapping for a child

    class.” 72 https://www.martinfowler.com/eaaCatalog/dependentMapping.html Dependent Mapping 依存マッピング
  73. x ∞ 顧客テーブル Ver.4 Dependent Object

  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 依存オブジェクトの特徴
  75. “Removes dependence upon problematic services during testing. WSDL ” 75

    https://www.martinfowler.com/eaaCatalog/dependentMapping.html Service Stub サービススタブ
  76. クラウドサインサービス

  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. プライマリアダプター
  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
  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 = ??? }
  80. Split endpoints moderately coarse grained. 80 荒い粒度でエンドポイントを分割する

  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 データ変換オブジェクト
  82. ドキュメントアセンブラ ドキュメントDTO

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

  84. Separation of Concerns 84 関心の分離

  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
  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 結論
  87. Now, you have learned main concept to write code of

         backend. 87 もうScalebaseのバックエンドを書けるはず
  88. Join us! ご応募お待ちしております!

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

  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 参考文献