Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Changing the mindset - more object-oriented view at the business domain modeling

A brief outline of the journey that software developer starts when meets contemporary business modeling techniques
by

Piotr Wyczesany

on 8 June 2014

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Changing the mindset - more object-oriented view at the business domain modeling

Changing the mindset
A brief outline of the journey
that software developer starts
when meets contemporary
business modeling techniques
by Piotr Wyczesany

@pwyczes
eventuallyinconsistent.com
What we will
be changing?
- approach to tackle complexity
- way we are modeling the business domain
- persistence techniques
- way we think about domain classes design
- our mindset
The example
domain
a football match
(probably we have a lot of experts here)
The Domain Expert says:
We can define a match as a meeting of two teams.

We can start the match at given time, but of course match can be started only once.

We can once finish the match with a score, but it cannot be finished before its start.
"
"
The user wants to see:
The match:
Team names that play

Match start date if is started

Match finish date if is finished

Score if match is finished
How would You start?
1. What would be the very first thing that You model out?
2. How would it be related
to the database?
3. How would You model
user actions that can be taken?
4. Would You even care about it
when modeling the domain...?
The Classic approach

@Entity
@Table
(name=
”matches”
)
public class
Match {

@Id
@Type
(type=
"org.hibernate.type.UUIDCharType"
)

private
UUID id;


@ManyToOne

private
Team homeTeam;


@Column
(updatable=
false
, insertable=
false)

private
String homeTeamId;


@ManyToOne

private
Team awayTeam;


@Column
(updatable=
false
, insertable=
false
)

private
String awayTeamId;


@Basic

private
Date matchDate;


@Basic

private
Date finishDate;


private
Score score;


// getters + setters
}



@Entity
@Table
(name=
”teams”
)
public class
Team {

@Id

private
String name;


// getters + setters
}


@Embeddable
public class
Score {

@Basic

private int
homeGoals;


@Basic

private int
awayGoals;


// getters + setters
}


public interface
MatchService {

void
createMatch(UUID matchId, String homeTeamId, String awayTeamId);


void
startMatch(UUID matchId, Date matchDate)

throws
MatchAlreadyStartedException;


void
finishMatch(
UUID matchId, int homeGoals, int awayGoals, Date finishDate)

throws
MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion;
}
We have:
classic entities

ORM mapping

private fields

public getters and setters

a Service

probably some tests checking MatchService
This example is extremely simple, but...
Is it Object-Oriented?
Any encapsulation here?
Maybe through
getters and
setters???
So what we can change?
and where to start?
not with the data...
...but with the language
The "Ubiquitous Language"
that is used by
Domain Experts
The Domain Expert says:
We can
define a match
as a meeting of
two teams
.

We can
start the match at given time
, but of course match can be
started only once
.

We can
once finish the match with a score
, but it
cannot be finished before its start
.
"
"
The word "match"
aggregates
everything around
the
Aggregate Root

Match entity
It will expose behavior
to the outside world...
...but will not expose
it's internals
Match can
be "defined"
Two "team names"
Team Value Object
We care only about value,
not id of the team
immutable
We can

"start the match at given time"
with... a method
brilliant, isn't it? ;)
but wait,
it can be done only once
an exception
"finish the match..."
another Value Object
Score
it cannot be finished
before start
- another Exception
and can be done
only once
@Embeddable
public class
Team {
@Basic

private
String name;


// constructor + getter
}
Value Objects
@Embeddable
public class
Score {

@Basic

private int
homeGoals;


@Basic

private int
awayGoals;


// constructor + getters
}
Match Aggregate Root
has own lifecycle
an ACID transaction boundary
get rid of dangerous setters
Score, Team and Date are immutable
we can leave
getters for them
at least for now ;)
to generate
the view somehow
@Entity
@Table
(name=
”matches”
)
public class
Match
extends
AggregateRoot {

@Id

private
UUID id;

@Basic

private
Date matchDate

@Basic

private
Date finishDate;

@Embedded

private
Team homeTeam;
@Embedded

private
Team awayTeam;
@Embedded

private
Score score;



public
Match(UUID id, Team homeTeam, Team awayTeam) {

this
.id = id;

this
.homeTeam = homeTeam;

this
.awayTeam = awayTeam;
}



public

void
startMatch(Date matchDate)
throws
MatchAlreadyStartedException {

if
(
this
.matchDate !=
null
) {

throw new
MatchAlreadyStartedException();
}

this
.matchDate = matchDate;
}


public

void
finishWithScore(Score score, Date finishDate)

throws
MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {


if
(matchDate ==
null
|| finishDate.before(matchDate)) {

throw new
MatchFinishedBeforeStartException();
}

if
(
this
.finishDate !=
null
) {

throw new
MatchAlreadyFinishedException();
}


this
.finishDate = finishDate;

this
.score = score;
}


// getters

}
and the code
...no Service here
code smell
what if we expose
mutable Entities
instead of
immutable Value Objects?
changing internals outside of transaction boundary
we achieved
verbose model

data connected with behavior

explicit and readable code

Ubiquitous Language in the code

OOP done right
Let's model the Domain!
what with those leaky getters?
imagine no getters at all...
but how to generate the view?
many views from different angles
looking at the same data
many joins in classic approach
Separate Models
Write Model
Read Model
Ubiquitous Language

Explicit transaction boundaries

Complex business logic
Optimized for each view

May be stored in different way (NoSQL?)

simple, well known anemic-like classes
Responsible for different user actions
Commands
Queries
How to feed it with data?
single database

database views
different data stores

we need something more
an Event
describes state transition
tells what happened
not what might happen
not what will happen
relatively small
without business logic
generated after
each business method call
sent to Queue
@Entity
@Table
(name=
”matches”
)
public class
Match
extends
AggregateRoot {

@Id

private
UUID id;

@Embedded

private
Team homeTeam;

@Embedded

private
Team awayTeam;

@Basic

private
Date matchDate

@Basic

private
Date finishDate

@Embedded

private
Score score;



public
Match(UUID id, Team homeTeam, Team awayTeam) {

this
.id = id;

this
.homeTeam = homeTeam;

this
.awayTeam = awayTeam;

apply(
new
MatchCreatedEvent(id, homeTeam.getName(), awayTeam.getName()));
}



public

void
startMatch(Date matchDate)
throws
MatchAlreadyStartedException {

if
(matchDate !=
null
) {

throw new
MatchAlreadyStartedException();
}

this
.matchDate = matchDate;

apply(
new
MatchStartedEvent(id, matchDate));
}



public

void
finishWithScore(Score score, Date finishDate)

throws
MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {

if
(matchDate ==
null
|| finishDate.before(matchDate)) {

throw new
MatchFinishedBeforeStartException();
}

if
(
this
.finishDate !=
null
) {

throw new
MatchAlreadyFinishedException();
}

this
.finishDate = finishDate;

this
.score = score;

apply(
new
MatchFinishedEvent(id, finshDate,
score.getHomeGoals(), score.getAwayGoals()));
}
}
the code:
...introduced events


...applied them at the end of the transaction


...sent them to the Queue (Event Bus)
we have...
Event
Handler
(re)populates the View Model
gets events after the transaction ends
eventually consistent
Event Store
The View Model is (re)populated
from Events
we have to store Events
data duplication on the Write Side
has to be consistent all the time
serialized events
single SQL table
NoSQL document database
Greg Young's
Event Store
Two sources of truth
for the Write Side
classic
SQL database
event
store
distributed
transaction?
Solution?
Get rid of one of
those databases
but which?
drop classic SQL database
scary, isn't it?
but imagine:
no more problems

with mappings
with joins
with queries
with database optimization
how to do it?
Event Sourcing
Aggregates are able
to recreate themselves
from past events
Repository
loads the Aggregate
not a DAO
applies all
historical events
or from snapshot
Aggregate needs changes
non-public methods
that apply events
each and every state transition
moved to those methods
we may remove
ORM annotations
No interface change
Only implementation
public class
Match
extends
AggregateRoot {

private
UUID id;

private
Team homeTeam;

private
Team awayTeam;

private
Date matchDate

private
Date finishDate

private
Score score;



public
Match(UUID id, Team homeTeam, Team awayTeam) {
apply(
new
MatchCreatedEvent(id, homeTeam.getName(), awayTeam.getName()));
}


private void
handle(MatchCreatedEvent event) {

this
.id = event.getId();

this
.homeTeam =
new
Team(event.getHomeTeamName());

this
.awayTeam =
new
Team(event.getAwayTeamName());
}



public void
startMatch(Date matchDate)
throws
MatchAlreadyStartedException {

if
(startDate !=
null
) {

throw new
MatchAlreadyStartedException();
}
apply(
new
MatchStartedEvent(id, matchDate));
}


private void
handle(MatchStartedEvent event) {

this
.matchDate = event.getMatchDate();
}



public void
finishWithScore(Score score, Date finishDate)

throws
MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {

if
(matchDate ==
null
|| finishDate.before(matchDate)) {

throw new
MatchFinishedBeforeStartException();
}

if
(
this
.finishDate !=
null
) {

throw new
MatchAlreadyFinishedException();
}
apply(
new
MatchFinishedEvent(
id, finshDate, score.getHomeGoals(), score.getAwayGoals()));
}


private void
handle(MatchFinishedEvent event) {

this
.finishDate = event.getFinishDate();

this
.score =
new
Score(event.getHomeGoals(), event.getAwayGoals());
}
}
Event Sourced Aggregate
We achieved:
only behavior exposed

aggregate recreated from past events

no ORM mappings
what about
historical data?
how hard it will be
to create
historical projection?
how hard it will be
to debug system
with production data
from specific
point of time?
what about
testing?
given :
a bunch of events

when :
a command

then :
a bunch of events
testing only
(and all)
behavior
BDD?
We are almost there...
... but we still need one final step
"never read locally"
on some fields
we can ignore them ;)
or we can ask "why?"
Subtle difference
The "aha" moment
fields in
Aggregate
they were here since
the very beginning...
...mapped directly
to SQL columns...
...loaded in each query...
...only to be shown...
...not used by any
business logic!
Modeling the Aggregate
responsibilities
interface
methods
not about fields!
think about
fields in Aggregate
we need only those that
will be used in making
business decisions
the rest of the data
is already stored
in Events
public class
Match
extends
AggregateRoot {

private
UUID id;

private
Date matchDate;

private

boolean
finished;



public
Match(UUID id, Team homeTeam, Team awayTeam) {
apply(
new
MatchCreatedEvent(id, homeTeam.getName(), awayTeam.getName()));
}


private void
handle(MatchCreatedEvent event) {

this
.id = event.getId();

this
.finished =
false
;
}



public void
startMatch(Date matchDate)
throws
MatchAlreadyStartedException {

if
(
this
.matchDate !=
null
) {

throw new
MatchAlreadyStartedException();
}
apply(
new
MatchStartedEvent(id, matchDate));
}


private void
handle(MatchStartedEvent event) {

this
.matchDate = event.getMatchDate();
}



public void
finishWithScore(Score score, Date finishDate)

throws
MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {

if
(matchDate ==
null
|| finishDate.before(matchDate)) {

throw new
MatchFinishedBeforeStartException();
}

if
(finished) {

throw new
MatchAlreadyFinishedException();
}
apply(
new
MatchFinishedEvent(
id, fin
i
shDate, score.getHomeGoals(), score.getAwayGoals()));
}


private void
handle(MatchFinishedEvent event) {

this
.finished =
true
;
}
}
The final code
How do you like it?
is it clean?

is it verbose?

is it explicit?

do you see what is the responsibility?

do you understand how it is persisted?
functional programming?
what is an Aggregate?
a bunch of functions with shared state
We are on the other side
of this bridge
let's look back
at the journey
Classic
approach
first think about
the data
separate data
from behavior
model was
the database schema
Turbo Pascal
for Enterprise
mainly asking "what?"
language
what we can "do"
with our model
use the language
in the code
behavior connected
to data
objects started
to expose behavior
leaky abstraction
with getters
"why?"
"what it does?"
"what is the responsibility
of this term?"
segregation of
responsibilities
commands
queries
Write Model
Read Model
Complex Business
Logic processing
Prepared
for each view
Data presentation
Rich model
what and where
we want to show?
does this functionality
change the state
of the system?
events
what happened
in the domain
state transitions
used as persistence
mechanism
whole history
thinking about business process instead of data
what changes given
behavior will imply?
How to name them?
How the business process looks like?
Domain-Driven Design
Command Query Responsibility Segregation
Event Sourcing
DDD/CQRS/ES
Thanks
Piotr Wyczesany

@pwyczes
eventuallyinconsistent.com

meetup.com/DDD-KRK

That's pretty much it...
Agenda
example domain

classic approach

language

modeling the domain

segregate responsibilities

events

subtle difference

retrospection of the journey
Classes:
Match
Team
Score
MatchService
what if we would do it
differently?
dispatcher
classic db
+ events
what if...
created
We can
"...with a score"
It will hold references
to other
aggregated entities
Tip of an iceberg
meetup.com/DDD-KRK
Domain-Driven Design
in a pill
Full transcript