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. Clean Architecture in Practice
    2019 ScalaMatsuri
    @yoshiyoshifujii
    Friday, June 28th
    17:00 ­ 17:40 Room C
    2019 ScalaMatsuri
    1 / 52

    View Slide

  2. 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

    View Slide

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

    View Slide

  4. 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

    View Slide

  5. 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

    View Slide

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

    View Slide

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

    View Slide

  8. 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

    View Slide

  9. 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

    View Slide

  10. 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

    View Slide

  11. 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

    View Slide

  12. Clean Architecture
    Clean Architecture in Practice
    2019 ScalaMatsuri
    11 / 52

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide


  24. Clean Architecture in Practice
    2019 ScalaMatsuri
    22 / 52

    View Slide

  25. 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

    View Slide

  26. Projects
    Clean Architecture in Practice
    2019 ScalaMatsuri
    24 / 52

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. 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

    View Slide

  30. Packages
    Clean Architecture in Practice
    2019 ScalaMatsuri
    26 / 52

    View Slide

  31. 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

    View Slide

  32. 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

    View Slide

  33. 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

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

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

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

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

    View Slide

  48. 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

    View Slide

  49. 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

    View Slide

  50. 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

    View Slide

  51. 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

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. 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

    View Slide

  55. 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

    View Slide

  56. 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

    View Slide

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

    View Slide