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

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

男爵
October 17, 2020

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

ScalaMatsuri2020の登壇資料です。

男爵

October 17, 2020
Tweet

More Decks by 男爵

Other Decks in Programming

Transcript

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

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

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

    requiring massive development ほぼWEBサービスのこと
  5. 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 目次
  6. This logic has to consider: • difference between regular and

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

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

    to handling old invoice • difference between total price and price paid partially Demand for Payment 支払催促のビジネスロジック
  9. 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の特徴
  10. 2. Architecture in MakeInvoice The way we have managed such

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

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

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

    any library or framework. Domain Layer ドメインレイヤー
  17. “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オブジェクト
  18. 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 }
  19. “An object model of the domain that incorporates both behavior

    and data.” 30 Domain Model https://www.martinfowler.com/eaaCatalog/domainModel.html ドメインモデル
  20. 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] = ??? }
  21. “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 値オブジェクト
  22. 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 = ??? }
  23. case class Address( countryCode: CountryCode, zipCode: ZipCode, city: String, address1:

    String, address2: String ) { def isSame(other: Address): Boolean = ??? }
  24. “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 リポジトリ
  25. 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 = ??? }
  26. What is EFF? 39 For more information, please go to

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

    ドメインモデルから設計を始める
  28. “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 ドメインモデル貧血症
  29. This layer contains business transaction only. So meaningful patterns adopted

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

    objects.” 47 https://www.martinfowler.com/eaaCatalog/mapper.html Mapper マッパー
  34. 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]] = ??? }
  35. Impedance Mismatch 51 Object Oriented World • Abstraction • Composition

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

    Relational DB World • Set • Relation 抽象化を集合と関係の世界へ
  37. “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 シングルテーブル継承
  38. “Represents an inheritance hierarchy of classes with one table for

    each class.” 59 https://www.martinfowler.com/eaaCatalog/classTableInheritance.html Class Table Inheritance クラステーブル継承
  39. Impedance Mismatch 61 Object Oriented World • Abstraction • Composition

    Relational DB World • Set • Relation コンポジションを集合と関係の世界へ
  40. “Maps an object into several fields of another object's table.”

    63 https://www.martinfowler.com/eaaCatalog/embeddedValue.html Embedded Value 組込バリュー
  41. “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
  42. • 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 シリアライズされたフィールドの功罪
  43. “Has one class perform the database mapping for a child

    class.” 72 https://www.martinfowler.com/eaaCatalog/dependentMapping.html Dependent Mapping 依存マッピング
  44. • 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 依存オブジェクトの特徴
  45. “Removes dependence upon problematic services during testing. WSDL ” 75

    https://www.martinfowler.com/eaaCatalog/dependentMapping.html Service Stub サービススタブ
  46. 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. プライマリアダプター
  47. “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
  48. 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 = ??? }
  49. “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 データ変換オブジェクト
  50. Domain Model Repository ValueObject DataMapper SingleTableInheritanse ClassTableInheritanse EmbededValue SerializedLOB DependentMapping

    DTO Remote Facade Putting It All Together 全てのパターンをひとつに SPA DB UseCase Service Stub Other SaaS
  51. 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 結論
  52. Now, you have learned main concept to write code of

         backend. 87 もうScalebaseのバックエンドを書けるはず
  53. 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 参考文献