CQRS Architecture on Android

CQRS Architecture on Android

LT @ shibuya.apk #23

0f8842fabcd31a6c9007ddcd648247db?s=128

Keita Kagurazaka

March 20, 2018
Tweet

Transcript

  1. CQRS Architecture on Android Keita Kagurazaka

  2. What is CQRS?

  3. CQRS • Command and Query Responsibility Segregation • Architecture extending

    CQS principle
  4. CQRS • Command and Query Responsibility Segregation • Architecture extending

    CQS principle
  5. Command Query Separation (CQS) principle • Design concept devised by

    Bertrand Meyer • We should divide an object’s methods into either Command or Query ◦ Commands change the state but return no value ◦ Queries return a value but never change the state
  6. Command Query Separation principle fun getCount(): Int { return this.count

    } fun incrementCount() { this.count++ } fun incrementCount(): Int { return ++this.count } Do. Don’t.
  7. Command Query Separation principle fun getCount(): Int { return this.count

    } fun incrementCount() { this.count++ } fun incrementCount(): Int { return ++this.count } Do. Don’t. Apply the concept to Architecture level
  8. Traditional Layered Architecture Presentation Layer Application Layer Domain Layer Infrastructure

    Layer
  9. CQRS Architecture Presentation Layer Application Layer Domain Layer Infrastructure Layer

    Command Stack Query Stack Application Layer Domain Layer Command Model Query Model c Infrastructure Layer
  10. Command Stack / Query Stack • Command Stack ◦ Change

    the system state ◦ Does not return the state to presentation layer • Query Stack ◦ Never change the system state ◦ Returns the presentation models to presentation layer
  11. Command Stack ≠ Objects that have only command methods

  12. Command Stack ≠ Objects that have only command methods Command

    Stack’s objects can have query methods to achieve changing the system state
  13. Query Stack ≒ Objects that have only query methods

  14. Why should we segregate Command stack and Query stack?

  15. Complexity is coming by unifying Domain Model • We have

    justified ourselves that Domain Model is complex because of Domain’s complexity • Segregation of Command and Query makes it easier to express Domain in Domain Model • CQRS lights up a new aspect of Domain
  16. Simple example: Conference app • User can show sessions, speakers

    and rooms info • User can make favorite sessions • All info are fetched by API • Use cache for improving user experience
  17. What is Domain Model? Session Speaker Room Entities

  18. How to construct repositories? class SessionRepository { fun findAll(): List<Session>

    = if (isInvalid) { isInvalid = false sessionApi.getAll().also(sessionDb::store) } else { sessionDb.findAll() } fun invalidate() { isInvald = true } ...
  19. How to construct repositories? class SessionRepository { fun findAll(): List<Session>

    = sessionDb.findAll() fun refresh() { sessionApi.getAll().also(sessionDb::store) } ...
  20. invalidate() or refresh() problem • Repository exposes the concern to

    API ◦ Against to “Collection-like interface for accessing entities” • Repository is going to have the domain logic
  21. Is it really problem? • Maybe no if in the

    small project • However the bad feeling implies that we still have room for improving
  22. Segregate models into Command and Query • Command Models -

    update the local storage ◦ Fetch data from API ◦ Write fetched data to the local storage • Query Models - show the latest info ◦ Retrieve the local storage ◦ Re-query when the local storage is changed
  23. Command Models Session DAO ApiClient Session is just a Value

    Object UseCase Domain
  24. UseCase as a TransactionScript class RefreshSessions { operator fun invoke()

    { val newSessions = sessionApi.getAll() sessionDb.store(newSessions) } ...
  25. Query Models Session Speaker Room Entities

  26. Query Models as Domain Models data class Session( val startTime:

    Date, val endTime: Date, … ) { fun getPeriodText(context: Context): String = context.getString(...) ...
  27. Query Models as Domain Models data class Session( val startTime:

    Date, val endTime: Date, … ) { fun getPeriodText(context: Context): String = context.getString(...) ... Can take Android Context since Query Domain Models concern presentation
  28. Query Models as Domain Models data class Session( val startTime:

    Date, val endTime: Date, … ) { fun getPeriodText(context: Context): String = context.getString(...) ... Can take Android Context since Query Domain Models concern presentation Avoid PresentationUtils class
  29. Re-query by observing Database changes • Some ORMs support observable

    queries ◦ Room - LiveData, RxJava ◦ Realm - Callback, RxJava ◦ Orma - RxJava, LiveData (in the plan) • Room also supports to use original classes instead of @Entity class in @Query
  30. CQRS Architecture on Android ViewModel UseCase CommandRepository Command Stack Query

    Stack Query Domain Models Change event QueryRepository
  31. Flux? View ActionCreators State Store Action

  32. CQRS vs Flux • Flux is a derivation of CQRS

    to optimize JavaScript world ◦ Non-complex command models ◦ No Database • Main difference is that the app state is stored on either database or memory (Store)
  33. Summary • CQRS is an architecture that segregate models into

    Command Stack and Query Stack • The separation makes it easier to construct models since understanding of Domain is definitely changed • CQRS and Flux are relatives
  34. Thanks!