Slide 1

Slide 1 text

CQRS Architecture on Android Keita Kagurazaka

Slide 2

Slide 2 text

What is CQRS?

Slide 3

Slide 3 text

CQRS ● Command and Query Responsibility Segregation ● Architecture extending CQS principle

Slide 4

Slide 4 text

CQRS ● Command and Query Responsibility Segregation ● Architecture extending CQS principle

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Traditional Layered Architecture Presentation Layer Application Layer Domain Layer Infrastructure Layer

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Command Stack ≠ Objects that have only command methods

Slide 12

Slide 12 text

Command Stack ≠ Objects that have only command methods Command Stack’s objects can have query methods to achieve changing the system state

Slide 13

Slide 13 text

Query Stack ≒ Objects that have only query methods

Slide 14

Slide 14 text

Why should we segregate Command stack and Query stack?

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

What is Domain Model? Session Speaker Room Entities

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

How to construct repositories? class SessionRepository { fun findAll(): List = sessionDb.findAll() fun refresh() { sessionApi.getAll().also(sessionDb::store) } ...

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Is it really problem? ● Maybe no if in the small project ● However the bad feeling implies that we still have room for improving

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Command Models Session DAO ApiClient Session is just a Value Object UseCase Domain

Slide 24

Slide 24 text

UseCase as a TransactionScript class RefreshSessions { operator fun invoke() { val newSessions = sessionApi.getAll() sessionDb.store(newSessions) } ...

Slide 25

Slide 25 text

Query Models Session Speaker Room Entities

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

CQRS Architecture on Android ViewModel UseCase CommandRepository Command Stack Query Stack Query Domain Models Change event QueryRepository

Slide 31

Slide 31 text

Flux? View ActionCreators State Store Action

Slide 32

Slide 32 text

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)

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Thanks!