Share
Embed
Transcript
90 slides
-
Who am I • Tanga Kenichi • @dnskimox • Backend
Developer • SaaS for contract management 2 自己紹介 -
PofEAA The book about powerful design patterns for “Enterprise Application”
written by Martin Fowler at 2002. 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 エンタープライズアプリケーションとは -
Enterprise Application ≒ Web Service 5 Especially a web service
requiring massive development ほぼWEBサービスのこと -
A web service of this session’s subject is... このセッションで取り上げる架空のWEBサービス *this
service is fictitious MakeInvoice -
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 目次 -
1. About MakeInvoice Service description about MakeInvoice, invoice creation and
management SaaS MakeInvoiceについて -
This logic has to consider: • difference between regular and
discount item • tax calculation • to manipulate a template of document Invoice Creation 請求書生成のビジネスロジック -
This logic has to consider: • partial payment • overpaying
• carrying over to next month Reconciliation 入金消込のビジネスロジック -
This logic has to consider: • resending invoice • how
to handling old invoice • difference between total price and price paid partially Demand for Payment 支払催促のビジネスロジック -
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の特徴 -
2. Architecture in MakeInvoice The way we have managed such
a large, complex application MakeInvoiceのアーキテクチャ -
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 各レイヤーの責務 依存の方向性 -
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 ヘキサゴナルアーキテクチャ -
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 プライマリアダプターと主アクター セカンダリアダプターと支援アクター -
3. Adoptable Patterns Design patterns that will be worthwhile to
adopt to MakeInvoice 適用可能なパターン -
Domain Layer UseCase Layer Primary / Secondary Adapter DataMapper, SingleTableInheritance,
EmbeddedValue, SerializedLOB, DependentMapping, DataTransferObject, ServiceStub (Nothing) DomainModel, POJO, ValueObject, Repository レイヤー毎のパターン -
Representing domain model is simple, unless domain classes depend on
any library or framework. Domain Layer ドメインレイヤー -
“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オブジェクト -
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 } -
“An object model of the domain that incorporates both behavior
and data.” 30 Domain Model https://www.martinfowler.com/eaaCatalog/domainModel.html ドメインモデル -
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] = ??? } -
“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 値オブジェクト -
case class Money( currency: Currency, value: BigDecimal ) case class
Currency( currencyCode: String ) -
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 = ??? } -
case class Address( countryCode: CountryCode, zipCode: ZipCode, city: String, address1:
String, address2: String ) { def isSame(other: Address): Boolean = ??? } -
“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 リポジトリ -
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 = ??? } -
What is EFF? 39 For more information, please go to
Yoshimura‘s session. EFFについて -
Design Domain Layer from Domain Model not from database. 40
ドメインモデルから設計を始める -
“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 ドメインモデル貧血症 -
This layer contains business transaction only. So meaningful patterns adopted
here would be honestly nothing. UseCase Layer ユースケースレイヤー -
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 () } -
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. セカンダリアダプター -
“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 データマッパー -
“An object that sets up a communication between two independent
objects.” 47 https://www.martinfowler.com/eaaCatalog/mapper.html Mapper マッパー -
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]] = ??? } -
Impedance Mismatch 51 Object Oriented World • Abstraction • Composition
Relational DB World • Set • Relation インピーダンスミスマッチ -
Impedance Mismatch 52 Object Oriented World • Abstraction • Composition
Relational DB World • Set • Relation 抽象化を集合と関係の世界へ -
“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 シングルテーブル継承 -
“Represents an inheritance hierarchy of classes with one table for
each class.” 59 https://www.martinfowler.com/eaaCatalog/classTableInheritance.html Class Table Inheritance クラステーブル継承 -
Impedance Mismatch 61 Object Oriented World • Abstraction • Composition
Relational DB World • Set • Relation コンポジションを集合と関係の世界へ -
“Maps an object into several fields of another object's table.”
63 https://www.martinfowler.com/eaaCatalog/embeddedValue.html Embedded Value 組込バリュー -
“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 -
• 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 シリアライズされたフィールドの功罪 -
“Has one class perform the database mapping for a child
class.” 72 https://www.martinfowler.com/eaaCatalog/dependentMapping.html Dependent Mapping 依存マッピング -
• 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 依存オブジェクトの特徴 -
“Removes dependence upon problematic services during testing. WSDL ” 75
https://www.martinfowler.com/eaaCatalog/dependentMapping.html Service Stub サービススタブ -
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. プライマリアダプター -
“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 -
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 = ??? } -
“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 データ変換オブジェクト -
Domain Model Repository ValueObject DataMapper SingleTableInheritanse ClassTableInheritanse EmbededValue SerializedLOB DependentMapping
DTO Remote Facade Putting It All Together 全てのパターンをひとつに SPA DB UseCase Service Stub Other SaaS -
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 結論 -
Now, you have learned main concept to write code of
backend. 87 もうScalebaseのバックエンドを書けるはず -
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 参考文献