Slide 1

Slide 1 text

Clean Architecture in Practice 2019 ScalaMatsuri @yoshiyoshifujii Friday, June 28th 17:00 ­ 17:40 Room C 2019 ScalaMatsuri 1 / 52

Slide 2

Slide 2 text

Yoshitaka Fujii @yoshiyoshifujii Chatwork Co., Ltd. Scala (5 years) Scala Kansai Summit staff (4 years ) 2016 ScalaMatsuri Heads­on Domain Driven Development in Scala 2017 ScalaMatsuri Let's Build a Serverless Architecture in Scala! 2019 Scala Fukuoka Clean Architecture in Practice Who am I ? Clean Architecture in Practice 2019 ScalaMatsuri 2 / 52

Slide 3

Slide 3 text

Clean Architecture 達人に学ぶソフトウェアの構造と設計 ­ 2018/07/27 Clean Architecture in Practice 2019 ScalaMatsuri 3 / 52

Slide 4

Slide 4 text

Author Robert C. Martin (Uncle Bob) Programmer from 1970 A co­founder of cleancoders.com, which provides online learning videos for software developers TheCleanCoder CleanCode UMLforJavaProgrammers AgileSoftwareDevelopment ExtremeProgramminginPractice Robert C .Martin, Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series) (English Edition) 1st Edition, Kindle. Clean Architecture in Practice 2019 ScalaMatsuri ボブおじさん。1970 年からプログラマー( 半世紀) 。cleancoders.com の共同創業者であり、ソフトウェア開発者向けの学習用動画をオ 4 / 52

Slide 5

Slide 5 text

The Clean Architecture [2012/08/13] The Clean Architecture ­ The Clean Code Blog https://blog.cleancoder.com/uncle­bob/2012/08/13/the­clean­architecture.html [2013/07/03] Uncle Bob: Architecture is About Intent, not Frameworks ­ InfoQ https://www.infoq.com/news/2013/07/architecture_intent_frameworks/ [2015/01/05] 持続可能な開発を目指す ~ ドメイン・ユースケース駆動(クリーンアーキテクチャ) + 単方向に制限した処 理 + FRP ­ Qiita https://qiita.com/kondei/items/41c28674c1bfd4156186 [2015/12/22] まだMVC,MVP,MVVM で消耗してるの? iOS Clean Architecture について ­ Qiita https://qiita.com/koutalou/items/07a4f9cf51a2d13e4cdc [2016/03/16] DDD + Clean Architecture + UCDOM Full 版 ­ Speakerdeck https://speakerdeck.com/yoskhdia/ddd­plus­clean­architecture­plus­ucdom­fullban [2018/11/07] ドメインオブジェクトを中心としたClean Architecture のためのレイヤー構成 ­ Qiita https://qiita.com/j5ik2o/items/7817055b35b2ff34f2e9 [2018/12/17] 実装クリーンアーキテクチャ ­ Qiita https://qiita.com/nrslib/items/a5f902c4defc83bd46b8 [2019/05/18] 実践クリーンアーキテクチャ with Java https://nrslib.com/clean­architecture­with­java/ Clean Architecture in Practice 2019 ScalaMatsuri 5 / 52

Slide 6

Slide 6 text

Agenda Why Clean Architecture ? Clean Architecture in Practice Clean Architecture in Practice 2019 ScalaMatsuri 6 / 52

Slide 7

Slide 7 text

Why Clean Architecture ? Clean Architecture in Practice 2019 ScalaMatsuri 7 / 52

Slide 8

Slide 8 text

Why Clean Architecture ? Any software system can be divided into Policies and Details Policies : All the rules and procedures of the business, the real value of system Details : DB, Web, Framework, etc. Do not affect the policy. Leave as many options as possible for as long as possible to keep the software soft. Leave out the unimportant details. Carefully distinguish between policy and detail, and design a policy that can defer or hold detailed decisions without the policy being dependent on the details Clean Architecture in Practice 2019 ScalaMatsuri あらゆるソフトウェアシステムは、 「方針」 と 「詳細」 に分割できる。ビジネスのすべてのルールや手順、システムの本当の価 7 / 52

Slide 9

Slide 9 text

Why Clean Architecture ? Any software system can be divided into Policies and Details Policies : All the rules and procedures of the business, the real value of system Details : DB, Web, Framework, etc. Do not affect the policy. Leave as many options as possible for as long as possible to keep the software soft. Leave out the unimportant details. Carefully distinguish between policy and detail, and design a policy that can defer or hold detailed decisions without the policy being dependent on the details Clean Architecture in Practice 2019 ScalaMatsuri ソフトウェアをソフトに保つために、できるだけ長い期間、多く選択肢を残す。残すべき選択肢とは、 重要でない詳細。方針と詳細 8 / 52

Slide 10

Slide 10 text

Why Clean Architecture ? Layered Architecture, Hexagonal Architecture also has the same purpose "Separation of Concerns" Framework independent, Testable, UI independent, Database independent, Foreign Agent independent Centrally express the results of model analysis by Domain Driven Design, and select Details later. Shouting Architecture Does the Architecture of the application shout "Health Care System" "Accounting System" Domain and Use Case interaction Clean Architecture in Practice 2019 ScalaMatsuri レイヤー化アーキテクチャ、ヘキサゴナルアーキテクチャも目的は同じ「関心事の分離」。ドメイン駆動設計によるモデル分析の結果 9 / 52

Slide 11

Slide 11 text

Why Clean Architecture ? Layered Architecture, Hexagonal Architecture also has the same purpose "Separation of Concerns" Framework independent, Testable, UI independent, Database independent, Foreign Agent independent Centrally express the results of model analysis by Domain Driven Design, and select Details later. Shouting Architecture Does the Architecture of the application shout "Health Care System" "Accounting System" Domain and Use Case interaction Build a long­term profitable system with a good Clean Architecture and Design Clean Architecture in Practice 2019 ScalaMatsuri 優れたクリーンなアーキテクチャと設計により長期的に利益をもたらすシステムを構築する 10 / 52

Slide 12

Slide 12 text

Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 11 / 52

Slide 13

Slide 13 text

Dependency rules Center of the circle, the higher the level of software. The outside of the circles is Details. Inside is Policies. Source code dependencies must be inward only. Does not affect anything from the outside of the circle to the inside. Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 依存性のルール。円の中央に近付くほどソフトウェアのレベルが上がる。円の外側は 詳細 。内側は 方針 。ソースコードの依存性 11 / 52

Slide 14

Slide 14 text

Entities Encapsulating the most important business rules. An object with a method. Even data structure and functions. Encapsulated the most common and top level rules. Changes in specific application operations are not affected. Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 最重要ビジネスルールをカプセル化したもの。メソッドを持ったオブジェクトでも、データ構造と関数でも可。最も一般的で最上位レ 12 / 52

Slide 15

Slide 15 text

Use Cases Contains application specific business rules. All use cases of the system are encapsulated and implemented. Adjust the flow of data input to and output from entities. Instruct entities to use the most important business rules. Changes in this layer do not affect entities. Unaffected by Databases, UI, Frameworks etc. Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri アプリケーション固有のビジネスルールが含まれている。システムの全てのユースケースがカプセル化・実装されている。エンティテ 13 / 52

Slide 16

Slide 16 text

Interface Adapters Layer to convert from convenient format to use cases and entities. As the destination of conversion, Database, Web etc. Conversely, convert from external format to internal format. Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri ユースケースやエンティティに便利なフォーマットから変換する層。変換する先としては、データベースやウェブなど。逆に、外部の 14 / 52

Slide 17

Slide 17 text

Frameworks & Drivers Outermost circle It consists of frameworks and tools Database and Web Framework etc. Usually, no code is written on this layer. Glue code to interact with the next inside of the circle, even if your write. Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 最も外側の円。フレームワークやツールで構成されている。データベースやウェブフレームワークなど。通常、このレイヤーにコード 15 / 52

Slide 18

Slide 18 text

About four circles Just outlined There will be more than four things needed There is no rule to admit anything other than these four However, dependency rules always apply. Source code dependencies always go inward. As you move inside the circle, the levels of abstraction and policy increase The outside of circle consists of specific details Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 概要を示しただけ。4 つ以外にも必要なものはあるだろうとのこと。この4 つ以外を認めないというルールはない。ただし、依存性のル 16 / 52

Slide 19

Slide 19 text

Cross the border Example of how to cross a circle border Controller and Presenter communicate with the Use Case of the inner layer Controller ­> Use Case ­> Presenter All dependencies are for the inner Use Case Use the Dependency inversion principle Organize interfaces and inheritance to reverse the flow of control Use polymorphism to not violate dependency rules Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri 円の境界線をどのように越えるべきかの例。Controller とPresenter は、内側のレイヤーのUse Case と通信している。依存関係は、すべ 17 / 52

Slide 20

Slide 20 text

Typical scenario Pass input data from user to Controller Controller stuffs data into POJO Input Data Pass to Use Case Interactor via Input Boundary Use Case Interactor controlls the dance of Entites. Database and I/O using Data Access Interface Generate Output Data as POJO and stuff the result Output Data passes to Presenter via Output Boundary interace Presenter repacks Output Data into ViewModel(POJO) Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri ユーザーからの入力データを、Controller に渡す。Controller は、POJO なInput Data にデータを詰め込む。Input Boundary を経由して、 18 / 52

Slide 21

Slide 21 text

Typical scenario Pass input data from user to Controller Controller stuffs data into POJO Input Data Pass to Use Case Interactor via Input Boundary Use Case Interactor controlls the dance of Entites. Database and I/O using Data Access Interface Generate Output Data as POJO and stuff the result Output Data passes to Presenter via Output Boundary interace Presenter repacks Output Data into ViewModel(POJO) Clean Architecture Clean Architecture in Practice 2019 ScalaMatsuri Data Access Interface を使用してDatabase とI/O する。Output Data をPOJO として生成し結果を詰め込む。Output Data は、Output 19 / 52

Slide 22

Slide 22 text

Based on this typical scenario, we will drop it into practical Scala code ↓ Source code is ↓ https://github.com/yoshiyoshifujii/scala­clean­architecture­example Clean Architecture in Practice 2019 ScalaMatsuri この典型的なシナリオをベースとして、実践的なScala のコードに落とし込んでいきます 20 / 52

Slide 23

Slide 23 text

This time, I tried to move closer to the code used in the field from typical scenarios. ↓ Source code is ↓ https://github.com/yoshiyoshifujii/scala­clean­architecture­example­v2 Clean Architecture in Practice 2019 ScalaMatsuri 今回は、典型的なシナリオから、より現場で使うコードに近づけてみました。 21 / 52

Slide 24

Slide 24 text

☕ Clean Architecture in Practice 2019 ScalaMatsuri 22 / 52

Slide 25

Slide 25 text

Simply Use Case is an interface that handles I/O. Controller has­a Presenter. Presenter is responsible for converting Output Data returned by Use Case to View Model Change the typical scenario Clean Architecture in Practice 2019 ScalaMatsuri シンプルにUse Case は、I/O を処理するインタフェースにした。Controller でPresenter を使うようにした。Presenter はUse Case が返 23 / 52

Slide 26

Slide 26 text

Projects Clean Architecture in Practice 2019 ScalaMatsuri 24 / 52

Slide 27

Slide 27 text

Projects build.sbt lazy val `infrastructure` = (project in file("modules/infrastructure")) lazy val `domain` = (project in file("modules/domain")).dependsOn(infrastructure) lazy val `usecases` = (project in file("modules/usecases")).dependsOn(domain) lazy val `interfaces` = (project in file("modules/interfaces")).dependsOn(usecases) lazy val `applications-http` = (project in file("applications/http")).dependsOn(interfaces) Clean Architecture in Practice 2019 ScalaMatsuri 24 / 52

Slide 28

Slide 28 text

Projects build.sbt lazy val `infrastructure` = (project in file("modules/infrastructure")) lazy val `domain` = (project in file("modules/domain")).dependsOn(infrastructure) lazy val `usecases` = (project in file("modules/usecases")).dependsOn(domain) lazy val `interfaces` = (project in file("modules/interfaces")).dependsOn(usecases) lazy val `applications-http` = (project in file("applications/http")).dependsOn(interfaces) infrastructure : Layered Architecture Infrastructure layer, not a layer that continues to depend as requirements change. A generic technology base on which all layers can depend. domain : Entities layer. usecases : As it is interfaces : Interface Adapters Layer applications-http : Where the ultimate details come in. Dependencies are injected into this project. Force the direction of dependency in sbt's multi project. Clean Architecture in Practice 2019 ScalaMatsuri レイヤー化アーキテクチャのインフラ層ではなく、要件が変化しても依存し続ける層。すべての層が依存できる汎用的な技術基盤。 24 / 52

Slide 29

Slide 29 text

Library Dependencies domain https://github.com/j5ik2o/scala­ddd­base "com.github.j5ik2o" %% "scala-ddd-base-core" % version A core library that abstracts DDD aggregation and repositories in a nice way Slick(JDBC) 、SkinnyORM(JCBC) 、Redis etc. Componentization of a typical part that tends to be a boiler plate https://github.com/septeni­original/sbt­dao­generator ­ Automatically generate DAO. It will reduce the work of the details. Recommend Clean Architecture in Practice 2019 ScalaMatsuri DDD の集約とリポジトリを良い感じに抽象化したcore ライブラリ。ボイラープレートが増えがちな詳細部分の作業を減らしてくれ 25 / 52

Slide 30

Slide 30 text

Packages Clean Architecture in Practice 2019 ScalaMatsuri 26 / 52

Slide 31

Slide 31 text

Domain Packaged with Ubiquitous Language and ValueObject Apart from common ValueObjects, they are grouped and packaged in ubiquitous language Incorporate domain modeling introduced in the ICONIX process Focus on objects in the real world (problem domain) Do not mistake domain models for data models Write a domain model before writing use cases to avoid vague names Packages Clean Architecture in Practice 2019 ScalaMatsuri ユビキタス言語やValueObject でパッケージ。共通的なValueObject 以外は、ユビキタス言語でグルーピング。ICONIX プロセスで紹介さ 26 / 52

Slide 32

Slide 32 text

Use Cases Under usecases package,UseCase is placed Organize use cases using actors and use case diagrams Cut the package for each use case actor Anonymous user's use case before signing in, anonymous Authorized actor use cases, signed Looking under the usecases package points to something that this system is shouting Packages Clean Architecture in Practice 2019 ScalaMatsuri usecases パッケージ直下に、 UseCase を配置している。アクターとユースケース図を使ってユースケースを組織化する。ユースケ 27 / 52

Slide 33

Slide 33 text

Use Cases Because repositories is application logic, put it in theusecases project When I implemented Eric Evans's layered archiving technology, I put it in the domain layer Since Clean Architecture has business logic as domain and application logic asusecases, we considered continuation as application logic Packages Clean Architecture in Practice 2019 ScalaMatsuri repositories は、アプリケーションロジック。Eric Evans のレイヤー化アーキテクチャを実践したときは、ドメイン層に置いた。 28 / 52

Slide 34

Slide 34 text

Interface Adapters Where conversion processing with detailed parts such as Framework, DB etc. Organize Controller and Presenter into controllers and presenters, respectively Put Repository in repositories Controller has a method that returns akka.http.scaladsl.Route if you decide to create a web with Akka HTTP. Presenter converts to POJO ViewModel Packaging parts for pluggable in their own role Packages Clean Architecture in Practice 2019 ScalaMatsuri Framework や、DB など、詳細な部分との変換処理が入るところ。プラガブルに使える部品をそれぞれの役割でパッケージングする 29 / 52

Slide 35

Slide 35 text

Interface Adapters The interfaces project may be further subdivided If it is a module that deploys to FaaS such as AWS Lambda, projects with strong dependencies on detailed libraries such as interfaces will get larger It is also conceivable to divide the project in detail into dependent libraries Policies are not affected if you change it here and there Packages Clean Architecture in Practice 2019 ScalaMatsuri interfaces プロジェクトは、さらに細分化しても良い。詳細なライブラリとの依存の強いプロジェクトはファイルサイズが大きく 30 / 52

Slide 36

Slide 36 text

View the implementation Clean Architecture in Practice 2019 ScalaMatsuri 31 / 52

Slide 37

Slide 37 text

UseCase has the type constructor Higher Kind F[_], InputData andOutputData The execute method takesInputData and UseCaseMonadError and returnsOutputData wrapped in F[_] Since UseCase always considers the case of fine day and rainy day, F[_] is expressed as MonadError Implement with Tagless Final Style UseCase trait UseCase[F[_], InputData, OutputData] { def execute(inputData: InputData)(implicit ME: UseCaseMonadError[F]): F[OutputData] } Clean Architecture in Practice 2019 ScalaMatsuri 型コンストラクタにHigher Kind F[_] 、任意のInputData とOutputData を持つ。UseCase は、必ず 晴れの日 と 雨の日 のケースを 32 / 52

Slide 38

Slide 38 text

UseCaseMonadError The error of MonadError is defined with type UseCaseError Add Type aliases to make them easy to handle After performing verification of the business logic encapsulated in the domain in the use case, prepare the process to convert to UseCaseMonadError by the enrich my library pattern package object usecases { type UseCaseMonadError[F[_]] = MonadError[F, UseCaseError] implicit class DomainError2MonadError[A](file:///Users/yoshiyoshifujii/workspace/git/yoshiyoshifujii/2019-06-28-scalamatsuri/val v: def toF[F[_]](file:///Users/yoshiyoshifujii/workspace/git/yoshiyoshifujii/2019-06-28-scalamatsuri/implicit ME: UseCaseMonadError[F v.fold( ne => ME.raiseError(UseCaseApplicationError(ne)), ME.pure ) } } Clean Architecture in Practice 2019 ScalaMatsuri MonadError のエラーをUseCaseError という型で定義している。ユースケース内でドメインにカプセル化したビジネスロジックの検証 33 / 52

Slide 39

Slide 39 text

UseCaseError sealed trait UseCaseError case class UseCaseSystemError(cause: Throwable) extends UseCaseError case class UseCaseApplicationError(message: String) extends UseCaseError UseCase handles SystemError due to a system error and those that are judged as ApplicationError as a result of Domain operation. Pattern matches in Presenter and converts to 4xx or 5xx errors I don't care about expressing UseCase for details such as synchronous or asynchronous, Either or Try Clean Architecture in Practice 2019 ScalaMatsuri UseCase では、Domain の操作をした結果、ApplicationError と判断されるものと、システム異常によるSystemError を扱う。Presenter 34 / 52

Slide 40

Slide 40 text

UseCaseInteractor class SignUpUseCase[F[_]]( accountRepository: AccountRepository[F], encryptService: EncryptService[F] ) extends UseCase[F, SignUpInput, SignUpOutput] { override def execute(inputData: SignUpInput)(implicit ME: UseCaseMonadError[F]): F[SignUpOutput] = ... } One class per Use Case Interface Separation Principle (ISP) Minimize dependency (Like a POJO) Receive Repository abstract in constructor The actual injection is the application-http project Assumes constructor injection in Airframe Clean Architecture in Practice 2019 ScalaMatsuri Use Case 1 つにつき、1 クラス。インターフェイス分離の原則(ISP )。コンストラクターにRepository の抽象を受けとる。Airframe 35 / 52

Slide 41

Slide 41 text

InputData / OutputData case class SignUpInput(email: Email, password: PlainPassword, name: AccountName) case class SignUpOutput(id: AccountId) The member variable type of InputData / OutputData is ValueObject of Domain Inspect Controller based on ValueObject's assertion specifications in Controller before passing to UseCase, and create ValueObject instance Clean Architecture in Practice 2019 ScalaMatsuri InputData / OutputData のメンバー変数の型は、Domain のValueObject 。UseCase に受け渡す前のController でValueObject の表明して 36 / 52

Slide 42

Slide 42 text

UseCaseInteractor inputData.email can get the checked value The system gets an account with Email, returns an error if it exists, and creates and saves an account if it does not exist. override def execute(inputData: SignUpInput)(implicit ME: UseCaseMonadError[F]): F[SignUpOutput] = for { maybe <- accountRepository.findBy(inputData.email) generated <- maybe match { case None => for { encrypted <- encryptService.encrypt(inputData.password.value.value) generated = Account.generate(AccountId(), inputData.email, inputData.name, EncryptedPassword(encrypted)) _ <- accountRepository.store(generated) } yield generated case Some(_) => ME.raiseError[GeneratedAccount](file:///Users/yoshiyoshifujii/workspace/git/yoshiyoshifujii/2019-06-28-scalamatsuri/UseCaseA } } yield SignUpOutput(generated.id) Clean Architecture in Practice 2019 ScalaMatsuri inputData.email は検査済みの値が取得できる。システムは、Email を持つアカウントを取得し、存在する場合はエラーを返し、存 37 / 52

Slide 43

Slide 43 text

Domain ­ business logic Explaining types in Refined The validate function returns an instance after performing validation at generation time To generate this domain model, it is necessary to follow the stated specification Generate a model after making sure that it meets the specifications stated at generation time final case class AccountName private[account] (value: AccountName.AsString) object AccountName extends ValueObject[String, AccountName] { type AsString = String Refined Size[Interval.Closed[W.`1`.T, W.`50`.T]] override val validate: String => DomainValidationResult[AccountName] = applyRef[AsString](file:///Users/yoshiyoshifujii/workspace/git/yoshiyoshifujii/2019-06-28-scalamatsuri/_).leftMap(DomainError).map } Clean Architecture in Practice 2019 ScalaMatsuri Refined で型を表明している。validate 関数は、生成時の検査を実施したうえでインスタンスを返却する。このドメインモデルを 38 / 52

Slide 44

Slide 44 text

ValueObject trait Validator[A, B] { val validate: A => DomainValidationResult[B] } trait Generator[A, B] { self: Validator[A, B] => val generate: A => B = validate(_).toOption.get } trait ValueObject[A, B] extends Validator[A, B] with Generator[A, B] ValueObject is expressed because it has validation and generation logic Clean Architecture in Practice 2019 ScalaMatsuri ValueObject は、検査と生成のロジックを持っているので表現した。 39 / 52

Slide 45

Slide 45 text

DomainValidationResult case class DomainError(message: String) type DomainValidationResult[A] = ValidatedNel[DomainError, A] Whether to accumulate and return inspection results of Domain or return inspection results as they are This time, as a result of confirming the pre­conditions, we accumulated the causes of the error and returned it at once Need to decide in advance to implement the policy Clean Architecture in Practice 2019 ScalaMatsuri Domain の検査結果を貯めて返却するか、検査結果をそのまま返すか。今回は、事前条件を確認した結果、エラーとなる原因を貯めて 40 / 52

Slide 46

Slide 46 text

Thus far, I’ve described the implementation of Policies. The remainder of this talk will be about Details implementation. Clean Architecture in Practice 2019 ScalaMatsuri ここまでは、 方針 の実装について解説した。以降は、 詳細 の実装について。 41 / 52

Slide 47

Slide 47 text

https://zio.dev/ Clean Architecture in Practice 2019 ScalaMatsuri ZIO 42 / 52

Slide 48

Slide 48 text

ZIO[R, E, A] type AppType = RDB with Redis type Effect[A] = ZIO[AppType, UseCaseError, A] The ZIO environment R combines several behaviors. Each behavior is composed of Module pattern. Determine the Higher Kind's F[_] with Effect[A]. Clean Architecture in Practice 2019 ScalaMatsuri ZIO の環境 R は、複数の振る舞いを合成する。各振る舞いはModule パターンで構成する。Higher Kind の F[_] を Effect[A] で決定 43 / 52

Slide 49

Slide 49 text

Controller trait Controller { import ValidateDirectives._ import adapters.errors.Errors._ private val runtime = bind[zio.Runtime[AppType]] private val signUpUseCase = bind[SignUpUseCase[Effect]] private val signUpPresenter = bind[SignUpPresenter] ... def toRoutes: Route = handleRejections(RejectionHandlers.default) { signUp ~ signIn ~ accountGets ~ accountGet ~ accountUpdate ~ accountDelete } private def signUp: Route = ... } Create a method for each endpoint. It is assumed that you use ZIO as Tagless Final Style's Higher kind. DI for UseCase and Presenter with Airframe. Clean Architecture in Practice 2019 ScalaMatsuri エンドポイントごとにメソッドを作る。Tagless Final Style の Higher kind にZIO を使った場合を想定している。Airframe でUseCase や 44 / 52

Slide 50

Slide 50 text

Controller private def signUp: Route = path("signup") { post { entity(as[SignUpRequestJson]) { json => validateJsonRequest(json).apply { inputData => signUpPresenter.response(signUpUseCase.execute(inputData)) } } } } Used akka-http-circe The result of executing UseCase is passed to Presenter and converted to ViewModel. The execution is done by Future and is handing over to Akka HTTP. Clean Architecture in Practice 2019 ScalaMatsuri akka-http-circe を使っている。UseCase を実行した結果をPresenter に渡してViewModel に変換している。実行はFuture で行い 45 / 52

Slide 51

Slide 51 text

Presenter trait Presenter[OutputData] { def response(res: Effect[OutputData])(implicit runtime: zio.Runtime[AppType]): Route = { val future = runtime.unsafeRunToFuture { res.foldM( handleError, success => ZIO.succeed(convert(success)) ) } onSuccess(future)(identity) } ... Responsible for converting any OutputData to a ViewModel Receive with Effect[OutputData] and set UseCaseError to E of ZIO[R, E, A] to Nothing UseCaseApplicationError => HTTP Status Code 4xxx. Need conversion. UseCaseSystemError => HTTP Status Code 5xx. Set to ZIO.fail ZIO.fail has a handler for Akka HTTP set to get 5xx error. Clean Architecture in Practice 2019 ScalaMatsuri 任意のOutputData をViewModel に変換する責務を持つ。Effect[OutputData] で受け取り、 ZIO[R, E, A] の E である 46 / 52

Slide 52

Slide 52 text

Presenter protected def convert(outputData: OutputData): Route protected def handleError(useCaseError: UseCaseError): Task[Route] = useCaseError match { case appError: UseCaseApplicationError => handleError(appError) case UseCaseSystemError(cause) => handleError(cause) } protected def handleError(useCaseApplicationError: UseCaseApplicationError): Task[Route] = Task.succeed { complete( HttpResponse( StatusCodes.BadRequest, entity = HttpEntity(contentType = ContentTypes.`application/json`, string = useCaseApplicationError.message) ) ) } protected def handleError(cause: Throwable): Task[Route] = Task.fail(cause) Clean Architecture in Practice 2019 ScalaMatsuri 47 / 52

Slide 53

Slide 53 text

DISettings ... private[adapters] def designOfRepositories: Design = newDesign.bind[AccountRepository[Effect]].to[AccountRepositoryBySlick] private[adapters] def designOfServices: Design = newDesign.bind[EncryptService[Effect]].to[EncryptServiceOnBCrypt] def design(environment: AppType, jwtSecret: String, jwtConfig: JwtConfig): Design = designOfRuntime(environment) .add(designOfSlick(environment.rdbService.profile, environment.rdbService.db)) .add(designOfRepositories) .add(designOfJwtConfig(jwtSecret, jwtConfig)) .add(designOfServices) Bind an entity such as Repository. If you know the default value without binding on Design side, it will build. UseCase, Presenter, Controller, etc. are set to automatic build. Clean Architecture in Practice 2019 ScalaMatsuri Repository などの実体をbind する。Design 側でbind しなくてもデフォルトの値が分かる場合はbuild してくれる。UseCase 、 48 / 52

Slide 54

Slide 54 text

Main object Main extends App { ... implicit val system: ActorSystem = ActorSystem("sample", config) implicit val materializer: ActorMaterializer = ActorMaterializer() implicit val executionContext: ExecutionContextExecutor = system.dispatcher private val environment: AppType = new RDB.Live with Redis.Live private val design = DISettings.design(environment) private val session = design.newSession session.start val controller = session.build[Controller] val bindingFuture = Http().bindAndHandle(controller.toRoutes, host, port) sys.addShutdownHook { session.shutdown bindingFuture .flatMap(_.unbind()) .onComplete(_ => system.terminate()) } } Clean Architecture in Practice 2019 ScalaMatsuri 49 / 52

Slide 55

Slide 55 text

Summary Any software system can be divided into Policies and Details Practicing Clean Architecture will allow you to carefully distinguish between policy and detail, and design a policy that can defer or hold detailed decisions without the policy being dependent on the details. Slow down the policy into code, validate, inspect, discuss and realize value Once the policy is in place, make details and realize pluggable soft architecture Clean Architecture in Practice 2019 ScalaMatsuri あらゆるソフトウェアシステムは、 「方針」 と 「詳細」 に分割できる。Clean Architecture を実践すると、方針と詳細を慎重に区別 50 / 52

Slide 56

Slide 56 text

Reference https://blog.cleancoder.com/uncle­bob/2012/08/13/the­clean­architecture.html https://www.infoq.com/jp/news/2013/07/architecture_intent_frameworks https://qiita.com/kondei/items/41c28674c1bfd4156186 https://qiita.com/koutalou/items/07a4f9cf51a2d13e4cdc https://speakerdeck.com/yoskhdia/ddd­plus­clean­architecture­plus­ucdom­fullban https://github.com/yoskhdia/cleanArchSample https://qiita.com/j5ik2o/items/7817055b35b2ff34f2e9 https://qiita.com/nrslib/items/a5f902c4defc83bd46b8 https://www.slideshare.net/AoiroAoino/purelyfunctionalplayframeworkapplication https://github.com/aoiroaoino/more­abstract­repository https://github.com/radusw/tagless­free­monix­samplep https://xuwei­k.hatenablog.com/entry/20140618/1403054751 Clean Architecture in Practice 2019 ScalaMatsuri 51 / 52

Slide 57

Slide 57 text

We're Hiring! https://corp.chatwork.com/ja/recruit/ Clean Architecture in Practice 2019 ScalaMatsuri 52 / 52