Kansai Summit staff (4 years ) 2016 ScalaMatsuri Headson 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
cofounder 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
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
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
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
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 longterm profitable system with a good Clean Architecture and Design Clean Architecture in Practice 2019 ScalaMatsuri 優れたクリーンなアーキテクチャと設計により長期的に利益をもたらすシステムを構築する 10 / 52
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
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
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
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
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
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
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
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
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
used in the field from typical scenarios. ↓ Source code is ↓ https://github.com/yoshiyoshifujii/scalacleanarchitectureexamplev2 Clean Architecture in Practice 2019 ScalaMatsuri 今回は、典型的なシナリオから、より現場で使うコードに近づけてみました。 21 / 52
hasa 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
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
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
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/septenioriginal/sbtdaogenerator Automatically generate DAO. It will reduce the work of the details. Recommend Clean Architecture in Practice 2019 ScalaMatsuri DDD の集約とリポジトリを良い感じに抽象化したcore ライブラリ。ボイラープレートが増えがちな詳細部分の作業を減らしてくれ 25 / 52
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
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
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
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
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
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
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
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
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
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
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
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
} 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
Whether to accumulate and return inspection results of Domain or return inspection results as they are This time, as a result of confirming the preconditions, 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
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
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
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
= { 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
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
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