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

Clean Architecture in Practice @ScalaMatsuri2019

Clean Architecture in Practice @ScalaMatsuri2019

実践 Clean Architecture
http://2019.scalamatsuri.org/

yoshiyoshifujii

June 28, 2019
Tweet

More Decks by yoshiyoshifujii

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. 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
  4. Agenda Why Clean Architecture ? Clean Architecture in Practice Clean

    Architecture in Practice 2019 ScalaMatsuri 6 / 52
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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