Unfortunately, the architectures of business applications are highly influenced by technology and infrastructure, even in 2016. Services and controllers are passed through the layers via Injection. Generators based on XML configurations or DSL’s create Voodoo code. Helpers (a.k.a. Managers or Utils) encapsulate major parts of the business logic. The more complex the application becomes and the more confusing the team structures is, the more difficult it gets to read the actual business logic from the source code. This works out well until the first error in the business logic or the first change request occurs. That’s when chaos starts. Normally only some small changes to the application are enough to put the focus back onto the business logic again. This session shows how to get to an architecture based on the business logic with only a few means. Wow-effects are guaranteed!"
2. Business vs. Technology
The Courage to Business Logic
Voxxed
Days
„May I submit my billing info
to your merchant account
via your payment gateway?“
offenkundiggut #WISSENTEILEN
4. The Courage to Business Logic
End User
Domain
Expert
Technical
Expert
Ubiquitous Language
„Common language as the
basis for a consistent
domain model.“
Business vs. Technology
Voxxed
Days
offenkundiggut #WISSENTEILEN
5. „Produce Software
that makes perfectly
sense to business,
not only to coder“
The Courage to Business Logic
End User
Domain
Expert
Technical
Expert
Rich Domain Model
Business vs. Technology
Voxxed
Days
offenkundiggut #WISSENTEILEN
6. „Produce Software
that is always
in a correct state,
not only partially“
The Courage to Business Logic
Domain
Expert
Technical
Expert
Rich Domain Model
Business vs. Technology
Access Layer
Business Layer
DB Layer
Voxxed
Days
offenkundiggut #WISSENTEILEN
7. The Courage to Business Logic
Reality Check
The déjà-vu
Experiment
Voxxed
Days
offenkundiggut #WISSENTEILEN
8. offenkundiggut #WISSENTEILEN
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
private String firstName;
private String lastName;
private Date birthDate;
private Address address;
public void setFirstName(String aFirstName) { ... }
public String getFirstName() { return firstName; }
public void setLastName(String aLastName) { ... }
public String getLastName() { return lastName; }
public void setBirthDate(Date birthDate) { ... }
public Date getBirthDate() { return birthDate; }
public void setAddress(Address address) { ... }
public Address getAddress() { return address; }
}
Reality Check
„Business“ Logic?
The Courage to Business Logic
Voxxed
Days
9. offenkundiggut #WISSENTEILEN
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
public Customer(String firstName,
String lastName,
String zipCode,
String city) { ... }
public Customer(String firstName,
String lastName,
String zipCode,
String city,
String street) { ... }
...
}
Reality Check
Parameter (Hell)?
The Courage to Business Logic
Constructor (Hell)?
Voxxed
Days
10. offenkundiggut #WISSENTEILEN
Reality Check
The Courage to Business Logic
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
public Customer(String,
String,
String,
String) { ... }
public Customer(String,
String,
String,
String,
String) { ... }
...
}
„no-Sources“ View
Voxxed
Days
11. offenkundiggut #WISSENTEILEN
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
public Customer(String firstName,
String lastName,
String zipCode,
String city) {
Validate.isTrue(StringUtils.isNotEmpty(firstName));
Validate.isTrue(StringUtils.isNotEmpty(sureName));
...
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
...
}
Reality Check
The Courage to Business Logic
Validation?
Voxxed
Days
12. offenkundiggut #WISSENTEILEN
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
private static final String ZIP_CODE_PATTERN = ...;
public Customer(String firstName,
String sureName,
String zipCode,
String city) {
setFirstName(firstName);
setZipCode(zipCode);
...
}
public void setZipCode(String zipCode) {
Validate.isTrue(Pattern.matches(ZIP_CODE_PATTERN, zipCode));
}
...
}
Reality Check
The Courage to Business Logic
Right place?
Voxxed
Days
13. offenkundiggut #WISSENTEILEN
/**
* Represents a controller to handle
* customer related changes
*/
public class CustomerController {
/**
* save new date of birth retrieved from the web
* in a customer entity
*/
public void alterBirthDate(Customer customer, String newDate) {
SimpleDateFormat df = new SimpleDateFormat();
Date birthDate = null;
try {
birthDate = df.parse(newDate);
} catch (ParseException e) {
// do something
}
Date today = new Date();
if (birthDate.after(today)) {
// do something
} else {
customer.setBirthDate(birthDate);
}
} ...
}
Reality Check
The Courage to Business Logic
Conversion?
Voxxed
Days
14. offenkundiggut #WISSENTEILEN
/**
* Represents a controller to handle
* customer related changes
*/
public class CustomerController {
/**
* check if a customer has birthday today
*/
public boolean hasBirthDay(Customer customer) {
Calendar todayCal = Calendar.getInstance();
Calendar birthDateCal = Calendar.getInstance();
birthDate.setTime(customer.getBirthDate().getTime());
if (birthDateCal.get(Calendar.YEAR) > (todayCal.get(Calendar.YEAR))) {
return false;
} else if (birthDateCal.get(Calendar.MONTH) !=
todayCal.get(Calendar.MONTH) ) {
return false;
} else if (birthDateCal.get(Calendar.DAY_OF_MONTH) != (todayCal
.get(Calendar.DAY_OF_MONTH))) {
return false;
}
return true;
}
...
}
Reality Check
The Courage to Business Logic
Evaluation?
Voxxed
Days
15. offenkundiggut #WISSENTEILEN
/**
* Util to calculate current age for a given date
* and to calculate if the date is a birthday today.
*/
public class BirthDayUtilAkaHelper {
public static boolean hasBirthDay(Date date) {
...
return ...;
}
public static int getAge(Date date) {
return 0;
}
}
/**
* usage of BirthdayUtil
*/
Customer customer = ...;
if (BirthDayUtilAkaHelper.hasBirthDay(customer.getBirthDate())) {
...;
}
Reality Check
The Courage to Business Logic
Util & Helper (Hell)?
a.k.a. Manager
Voxxed
Days
16. offenkundiggut #WISSENTEILEN
/**
* Represents a business customer with a
* full qualified name, a date of birth indicating
* if the customer is „sui juris“ and a valid address
* that may change after a relocation.
*/
public class Customer {
private Date birthDate;
public void setBirthDate(Date newBirthDate) {
if (isInFuture(newBirthDate)) { /*abort */ ... }
birthDate = newBirthDate;
}
public Date getBirthDate() {
return birthDate;
}
...
}
/**
* usage of getBirthDate
*/
Customer customer = ...;
customer.getBirthDate().setTime(someTimeInFuture);
Reality Check
Immutable?
The Courage to Business Logic
Voxxed
Days
17. offenkundiggut #WISSENTEILEN
/**
* Data Access Object for retrieving
* customer objects from the DB.
*/
public class CustomerDao {
public List<Customer> findByZipCodeOrCity(String zipCode, String city) {
return ...;
}
public List<Customer> findByZipCodeOrCityWithLimit(String zipCode,
String city, int limit) {
return ...;
}
public List<Customer> findByZipCodeOrCityAndOrders(String zipCode,
String city, int orderCount) {
return ...;
}
}
/**
* usage of getBirthDate
*/
String current= ...;
String value = ...;
List<Customer> searchResult = dao.findByZipCodeOrCity(current,value);
Reality Check
Meaningful API?
The Courage to Business Logic
Correct order?
Voxxed
Days
18. offenkundiggut #WISSENTEILEN
/**
* Data Access Object for retrieving
* customer objects from the DB.
*/
public class CustomerDao {
// BEFORE refactoring
public List<Customer> findByZipCodeOrCity(String zipCode,
String city) {
return ...;
}
// AFTER refactoring
public List<Customer> findByZipCodeOrCity(String zipCode,
String city,
String street) {
return ...;
}
...;
}
Reality Check
Refactoring Pitfall!
The Courage to Business Logic
Voxxed
Days
19. The Courage to Business Logic
Problem Areas
Voxxed
Days
offenkundiggut #WISSENTEILEN
20. The Courage to Business Logic
Problem Areas
Result of the Reality Check
‣ Anemic Domain Model
‣ non-meaningful API
‣ missing Null Value Handling
‣ inconsequent or missing Validation
‣ duplicated Code
‣ Util, Helper & Manager Hell
Voxxed
Days
Absence of
Code Stability!
offenkundiggut #WISSENTEILEN
21. The Courage to Business Logic
Problem Areas
Absence of Code Stability
‣ Team growth / reduction becomes a high Risk
‣ Where can i find the business logic?
‣ What the hell is this class doing exactly?
‣ Where do this stuff belongs to?
‣ Copy & Paste Design Pattern!
Voxxed
Days
offenkundiggut #WISSENTEILEN
22. The Courage to Business Logic
Survival Guide
Voxxed
Days
offenkundiggut #WISSENTEILEN
23. Just as a Reminder
„We want a meaningful Domain Model
with an descriptive API, which can be
created and kept consistent during
its whole lifetime.“
The Courage to Business Logic
Survival Guide
Voxxed
Days
offenkundiggut #WISSENTEILEN
24. The Courage to Business Logic
Survival Guide
Requirements
‣ meaningful Model
‣ descriptive API
‣ create consistently
‣ keep consistently
Voxxed
Days
offenkundiggut #WISSENTEILEN
25. The Courage to Business Logic
Survival Guide
Rich Domain Model
‣ Business Logic
‣ Validation
‣ Conversion
‣ Normalization
‣ Consistency Checks
Voxxed
Days
offenkundiggut #WISSENTEILEN
26. The Courage to Business Logic
Survival Guide
Refactoring Step by Step
‣ Step 1: Value Objects
‣ Step 2: Rich Entities
‣ Step 3: Builder Pattern
Voxxed
Days
offenkundiggut #WISSENTEILEN
27. The Courage to Business Logic
Step 1: Value Objects
Voxxed
Days
offenkundiggut #WISSENTEILEN
28. The Courage to Business Logic
Step 1: Value Objects
Voxxed
Days
„An object that contains
attributes but has
no conceptual identity.
They should be treated
as immutable.“
offenkundiggut #WISSENTEILEN
29. The Courage to Business Logic
Step 1: Value Objects
Value Objects …
‣ #1: are Objects, not Primitives
‣ #2: are immutable
‣ #3: are doing business relevant conversion
‣ #4: are doing business relevant normalization
‣ #5: are doing business relevant validation
‣ #6: are „full blown“ Objects
Voxxed
Days
offenkundiggut #WISSENTEILEN
30. /**
* #1: Value Objects are Objects, not Primitives
*/
public class Customer {
private Date birthDate;
...
)
Step 1: Value Objects
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
31. /**
* #1: Value Objects are Objects, not Primitives
*/
public class Customer {
private DateOfBirth birthDate;
...
)
„more than just a Date“
Step 1: Value Objects
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
32. /**
* Represents a date of birth
*/
public class DateOfBirth {
private Date dateOfBirth;
public boolean hasBirthDay() {
return ...;
}
public int getCurrentAge() {
return ...;
}
}
Step 1: Value Objects
The Courage to Business Logic
Voxxed
Days
„more than just a Date“
offenkundiggut #WISSENTEILEN
33. offenkundiggut #WISSENTEILEN
/**
* #2: Value Objects are immutable
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) {
// do some validation here and
// throw IllegalArgumentException if fail
...;
this.dateOfBirth = date;
}
public Date getDate() {
return new Date(dateOfBirth.getTime());
}
...
}
Step 1: Value Objects
Immutable!
The Courage to Business Logic
Voxxed
Days
34. offenkundiggut #WISSENTEILEN
/**
* #3: Value Objects are doing business relevant conversion
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) {
// do some validation here and
// throw IllegalArgumentException if fail
...;
this.dateOfBirth = date;
}
public DateOfBirth(String date) {
// create date of birth from "date" string with default pattern
...;
}
public DateOfBirth(String date, String pattern) {
// create date of birth from "date" string with "pattern"
...;
}
...
}
Step 1: Value Objects
Conversion!
The Courage to Business Logic
Voxxed
Days
35. offenkundiggut #WISSENTEILEN
/**
* #4: Value Objects are doing business relevant normalization
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) {
// do some validation here and
// throw IllegalArgumentException if fail
...;
this.dateOfBirth = copyAndStripTimeStamp(date);
}
private Date copyAndStripTimeStamp(Date date) {
// strip timestamp and copy result in a new Date object
return ...;
}
...
}
Step 1: Value Objects
Normalization!
The Courage to Business Logic
Voxxed
Days
36. offenkundiggut #WISSENTEILEN
/**
* #5: Value Objects are doing business relevant validation
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) {
Date today = new Date();
if (date.after(today)) {
throw new IllegalArgumentException("Birthday " + date
+ " may not be after current time " + today);
}
this.dateOfBirth = copyAndStripTimeStamp(date);
}
private Date copyAndStripTimeStamp(Date date) {
// strip timestamp and copy result in a new Date object
return ...;
}
...
}
Step 1: Value Objects
Validation!
The Courage to Business Logic
Voxxed
Days
37. offenkundiggut #WISSENTEILEN
/**
* #6: Value Objects are „full blown“ Objects
*/
public class DateOfBirth implements Serializable, Comparable{
private Date dateOfBirth;
...
@Override
public String toString() {
return ...;
}
@Override
public int hashCode() {
return ...;
}
@Override
public boolean equals(Object obj) {
return ...;
}
public int compareTo(Object o) {
return ...;
}
}
Step 1: Value Objects
Full blown Object!
The Courage to Business Logic
Voxxed
Days
38. The Courage to Business Logic
Step 1: Value Objects
Value Objects Examples
‣ FirstName, LastName, DateOfBirth
‣ ZipCode, City, Country
‣ Email, PhoneNumber
‣ Note, Description
‣ Year, Month, …
‣ Money, …
Voxxed
Days
offenkundiggut #WISSENTEILEN
39. The Courage to Business Logic
Step 1: Value Objects
Value Objects Benefits
‣ Business Code is part of the Domain
‣ Non / nearly non duplicated Code
‣ meaningful & consistent Code
‣ readable & maintainable Code
‣ No more Util, Helper, Manager & Friends
Voxxed
Days
offenkundiggut #WISSENTEILEN
40. The Courage to Business Logic
Step 2: Rich Entities
Voxxed
Days
offenkundiggut #WISSENTEILEN
41. The Courage to Business Logic
Step 2: Rich Entities
Voxxed
Days
„An object that is not
defined by its
attributes, but rather
by a thread of
continuity and its
identity.“
offenkundiggut #WISSENTEILEN
42. The Courage to Business Logic
Step 2: Rich Entities
„Golden Rules“ for rich Entities …
‣ #1: are consistent
‣ #2: Attributes are Value Objects, not Primitives
‣ #3: Setter only if needed by Business Logic
‣ #4: Methods are Business relevant Operations
Voxxed
Days
offenkundiggut #WISSENTEILEN
43. /**
* #1: Entities are consistent
*
*/
public class Customer {
private String lastName;
private Customer(String name) {
lastName = notNull(name);
};
...
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
commons.lang3.Validate
offenkundiggut #WISSENTEILEN
44. /**
* #2: Attributes are Value Objects, not Primitives
*/
public class Customer {
private String lastName;
…
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
45. /**
* #2: Attributes are Value Objects, not Primitives
*/
public class Customer {
private LastName lastName;
…
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
46. /**
* #3: Setter only if needed by Business Logic
*
*/
public class Customer {
private LastName lastName;
public Customer(LastName name) {
lastName = setLastname(name);
};
public void setLastName(LastName name) {
lastName = notNull(name);
};
...
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
BTW: Validation
offenkundiggut #WISSENTEILEN
47. /**
* #4: Methods are business relevant operations
*/
public class Customer {
private LastName lastName;
private boolean married;
private Person partner;
public void setLastName(LastName name) { … };
public void setMarried(boolean married) { … };
public void setPartner(Partner partner) { … };
…
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
48. /**
* #4: Methods are business relevant operations
*/
public class Customer {
private LastName lastName;
private FamilyStatus familyStatus;
private Person partner;
public void marry(Person partner, LastName aLastName) {
lastName = notNull(aLastName)
familyStatus = FamilyStatus.MARRIED;
partner = ...; //guarantee bidirectional consistency!
};
…
)
Step 2: Rich Entities
The Courage to Business Logic
Voxxed
Days
offenkundiggut #WISSENTEILEN
49. The Courage to Business Logic
Step 3: Builder Pattern
Voxxed
Days
offenkundiggut #WISSENTEILEN
50. The Courage to Business Logic
Step 3: Builder Pattern
Builder Pattern in a Nutshell
‣ Object Creation Design Pattern
‣ Answer to Constructor-Hell Anti-Pattern
‣ Step-by-Step approach
‣ Good candidate for a Fluent-API
Voxxed
Days
offenkundiggut #WISSENTEILEN
51. // Builder Pattern - Poor Mans Edition
public class Address {
public Address(String street,
String streetNumber,
String zipCode,
String city,
String state,
String country) {
Validate.isTrue(StringUtils.isNotEmpty(streetNumber));
Validate.isTrue(StringUtils.isNotEmpty(zipCode));
Validate.isTrue(StringUtils.isNotEmpty(city));
Validate.isTrue(StringUtils.isNotEmpty(state));
Validate.isTrue(StringUtils.isNotEmpty(country));
...
}
// value object is immutable
// - no setter
// - but getter
...;
}
Step 3: Builder Pattern
The Courage to Business Logic
Nice? Not really!
Voxxed
Days
offenkundiggut #WISSENTEILEN
52. // Builder Pattern - Simple Edition IN ACTION
// create AddressBuilder
AddressBuilder builder = new AddressBuilder();
// set all known values
// BTW: could be also used in JSF and other frameworks
builder.setStreet(...);
builder.setStreetNumber(...);
builder.setZipCode(...);
builder.setCity(...);
builder.setState(...);
builder.setCountry(...);
// build valid and immutable Address object
Address address = builder.build();
Step 3: Builder Pattern
The Courage to Business Logic
Nicer?
Voxxed
Days
offenkundiggut #WISSENTEILEN
53. offenkundiggut #WISSENTEILEN
// Builder Pattern - Simple Edition
public class AddressBuilder {
private Street street;
private StreetNumber streetNumber;
private ZipCode zipCode;
private City city;
private State state;
private Country country;
public Address build() {
// validate single parameter and cross parameter constraints
// and throw exception if any problem occurs
...
// create valid Address object else
return new Address(...);
}
public void setStreet(Street street) {
this.street = street;
}
public void setStreetNumber(StreetNumber streetNumber) {
this.streetNumber = streetNumber;
}
...
}
Step 3: Builder Pattern
The Courage to Business Logic
Kind of!
Voxxed
Days
54. // Builder Pattern - Extended Edition IN ACTION
// create AddressBuilder
Address address = Adress.newAddress()
// set values
.forStreetNumber(...).ofStreet(...)
.inCity(...).withZipCode(...)
.lyingInState(...).ofCountry(...)
// build VALID Address object
.build(...);
Step 3: Builder Pattern
The Courage to Business Logic
Nicest?
Voxxed
Days
offenkundiggut #WISSENTEILEN
55. // Builder Pattern - Extended Edition with Inner Class Builder
public class Address {
private Address() { /* empty constructor to prohibit use */ }
public static Builder newAddress() {
return new Address().new Builder();
}
private String street; ... // some more attributes
public class Builder {
public Builder ofStreet(String initialStreet) {
street = validateStreet(initialStreet);
return this;
}
... // some more methods to fill the Builder
public Address build() {
validate();
return Address.this;
}
}
}
Step 3: Builder Pattern
The Courage to Business Logic
Voxxed
Days
YES!
offenkundiggut #WISSENTEILEN
56. The Courage to Business Logic
Conclusion
Voxxed
Days
offenkundiggut #WISSENTEILEN
The Design is the Code,
the Code is the Design.
57. „Do i have to change my
Code if the Business
Logic changes?“
The Courage to Business Logic
Conclusion
Voxxed
Days
offenkundiggut #WISSENTEILEN
58. „Why the hell NOT?
The Code is
the Business Logic!“
The Courage to Business Logic
Conclusion
Voxxed
Days
offenkundiggut #WISSENTEILEN