Leider sind in der "freien Wildbahn" viele Architekturen und APIs stark durch die verwendeten Technologien, Frameworks, Generatoren etc. getrieben. Die eigentliche Fachlichkeit der Anwendung bleibt dabei häufig auf der Strecke und ist im Quellcode kaum noch auffindbar. Das Chaos beim Bug-Fixing ist entsprechend vorprogrammiert.
Die Session zeigt einfache, aber sehr effektive Strategien, wie bestehende Anwendungen in ein fachlich getriebenes Architektur- und API-Design überführt werden können - Aha-Effekte garantiert.
2. Business vs. Technology
Mut zur Fachlichkeit
Java
Land
„May I submit my billing info to your
merchant account via your payment
gateway?“
offenkundiggut #WISSENTEILEN
4. Mut zur Fachlichkeit
End User
Domain
Expert
Technical
Expert
Business vs. Technology
Translation
ist auf Dauer teuer
- sogar sehr teuer!
offenkundiggut #WISSENTEILEN
Java
Land
5. Mut zur Fachlichkeit
Java
Land
End User
Domain
Expert
Technical
Expert
Ubiquitous Language
Basis für ein
konsistentes
Domain Model
Business vs. Technology
offenkundiggut #WISSENTEILEN
6. „Produce Software
that makes perfectly
sense to business,
not only to coder“
Mut zur Fachlichkeit
Java
Land
End User
Domain
Expert
Technical
Expert
Rich Domain Model
Business vs. Technology
offenkundiggut #WISSENTEILEN
7. „Produce Software
that is always
in a correct state,
not only partially“
Mut zur Fachlichkeit
Java
Land
Domain
Expert
Technical
Expert
Rich Domain Model
Business vs. Technology
Access Layer
Business Layer
DB Layer
offenkundiggut #WISSENTEILEN
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 {
private String firstName;
private String sureName;
private Date birthDate;
private Address address;
public void setFirstName(String aFirstName) { ... }
public String getFirstName() { return firstName; }
public void setSureName(String aSureName) { ... }
public String getSureName() { return sureName; }
public void setBirthDate(Date birthDate) { ... }
public Date getBirthDate() { return birthDate; }
public void setAddress(Address address) { ... }
public Address getAddress() { return address; }
}
Reality Check
Fachlichkeit?
Java
Land Mut zur Fachlichkeit
10. 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 sureName,
String zipCode,
String city) { ... }
public Customer(String firstName,
String sureName,
String zipCode,
String city,
String street) { ... }
...
}
Reality Check
Parameter (Hölle)?
Java
Land Mut zur Fachlichkeit
Konstruktor (Hölle)?
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 sureName,
String zipCode,
String city) {
Validate.isTrue(StringUtils.isNotEmpty(firstName));
Validate.isTrue(StringUtils.isNotEmpty(sureName));
...
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
...
}
Reality CheckJava
Land Mut zur Fachlichkeit
Validierung?
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 ZCODE_PATTERN = ...;
public Customer(String firstName,
String sureName,
String zipCode,
String city) {
setFirstName(firstName);
setZipCode(zipCode);
...
}
public void setZipCode(String zipCode) {
Validate.isTrue(Pattern.matches(ZCODE_PATTERN, zipCode));
}
...
}
Reality CheckJava
Land Mut zur Fachlichkeit
Richtig hier?
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 CheckJava
Land Mut zur Fachlichkeit
Konvertierung?
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 CheckJava
Land Mut zur Fachlichkeit
Auswertung?
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 CheckJava
Land Mut zur Fachlichkeit
Util & Helper (Hölle)?
a.k.a. Manager
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?
Java
Land Mut zur Fachlichkeit
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?
Java
Land Mut zur Fachlichkeit
Correct Order?
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?
Java
Land Mut zur Fachlichkeit
20. Java
Land Mut zur Fachlichkeit
Problemfelder
Resultat des „Reality Check“
‣ Anemic Domain Model
‣ wenig aussagekräftige APIs
‣ fehlendes Null-Value Handling
‣ inkonsequente oder fehlende Validierung
‣ duplizierter Code
‣ Util, Helper & Manager Hölle
Fehlende
Robustheit!
offenkundiggut #WISSENTEILEN
21. Mut zur Fachlichkeit
Problemfelder
Fehlende Robustheit
‣ Teamwachstum / -fluktuation als Risiko
‣ Wo finde ich was?
‣ Was macht das?
‣ Wo gehört das hin?
‣ Copy & Paste Design Pattern!
Java
Land
offenkundiggut #WISSENTEILEN
23. Zur Erinnerung
„Wir möchten ein sprechendes Domain Model,
mit einem sprechenden API, welches wir in sich
konsistent erzeugen und im weiteren Verlauf
konsequent konsistent erhalten.“
Mut zur Fachlichkeit
LösungsansätzeJava
Land
offenkundiggut #WISSENTEILEN
24. Mut zur Fachlichkeit
Lösungsansätze
Rich Domain Model
‣ inkl. Fachlichkeit
‣ inkl. Validierung
‣ inkl. Konvertierung
‣ inkl. Normalisierung
‣ inkl. Konsistenzprüfung
Java
Land
offenkundiggut #WISSENTEILEN
26. Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
27. Rich Entities
„An object that is not
defined by its
attributes, but rather
by a thread of
continuity and its
identity.“
Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
28. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entity Charakteristika (1/2)
‣ Kontinuität über einen Zeitraum
‣ Existenz einer eindeutigen Identität, mit
(fachlichem) Schlüssel, z.B. User-Name
‣ Änderung der Attribute ändern die Identität
nicht, d.h. Identität bleibt konstant
Rich EntitiesJava
Land
29. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entity Charakteristika (2/2)
‣ Entitäten mit gleichen Attributen sind nicht
zwingend gleich
‣ Entitäten mit verschiedenen Attributen können
dennoch identisch sein
‣ Vorsicht bei Operationen, die Entitäten über
Attribute vergleichen
Rich EntitiesJava
Land
30. Mut zur Fachlichkeit
„Golden Rules“ für Entitäten …
‣ #1: sind in sich konsistent
‣ #2: setter nur für fachlich änderbare Attribute
‣ #3: enthalten ihre Fachlichkeit
‣ #4: sind fachliche Objekte
offenkundiggut #WISSENTEILEN
Rich EntitiesJava
Land
31. /**
* #1: Entitäten sind in sich konsistent
*
*/
public class Customer {
private String lastName;
private Customer(String name) {
lastName = notNull(name);
};
...
)
commons.lang3.Validate
Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
32. BTW: Validation
Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
/**
* #2: Nur fachlich änderbare Attribute haben Setter
*
*/
public class Customer {
private String lastName;
public Customer(String name) {
lastName = setLastname(name);
};
public void setLastName(String name) {
lastName = notNull(name);
};
...
)
33. Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
/**
* #3: Entitäten enthalten ihre Fachlichkeit. d.h.
* Attributänderungen sind fachliche Operationen
*/
public class Customer extends Person {
private String lastName;
private boolean married;
private Person partner;
public void setLastName(String name) { … };
public void setMarried(boolean married) { … };
public void setPartner(Partner partner) { … };
…
)
34. Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
/**
* #3: Entitäten enthalten ihre Fachlichkeit. d.h.
* Attributänderungen sind fachliche Operationen
*/
public class Customer extends Person {
private String lastName;
private FamilyStatus familyStatus;
private Person partner;
public void marry(Person aPartner, String aLastName) {
lastName = notNull(aLastName)
familyStatus = FamilyStatus.MARRIED;
partner = ...; //guarantee bidirectional consistency!
};
…
)
35. /**
* #4: Entities sind fachliche Objekte.Eigenschaften
* repräsentieren Fachlichkeit und sind nur in
* Ausnahmefällen „Primitive.
*/
public class Customer extends Person {
private String lastName;
…
)
Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich EntitiesJava
Land
36. /**
* #4: Entities sind fachliche Objekte.Eigenschaften
* repräsentieren Fachlichkeit und sind nur in
* Ausnahmefällen „Primitive.
*/
public class Customer extends Person {
private LastName lastName;
…
)
Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Rich Entities
Was ist das, bitte?
Java
Land
38. „An object that contains
attributes but has
no conceptual identity.
They should be treated
as immutable.“
Java
Land Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Value Objects
39. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Value Objects Charakteristika (1/2)
‣ Keine konzeptionelle Identität
‣ Beschreibung von Charakteristika
‣ „was“ statt „wer“?
‣ Gleichheit via Gleichheit der Attribute definiert
‣ Unveränderlich
‣ Daten sind zu jedem Zeitpunkt konsistent
Value ObjectsJava
Land
40. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Value Objects Charakteristika (2/2)
‣ Operationen auf Value Objects
‣ In sich abgeschlossen
‣ Rückgabe-Wert ist eine neue Instanz mit
geändertem Wert
‣ Beispiele aus Java
‣ java.lang.String, java.math.BigInteger, …
Value ObjectsJava
Land
41. Mut zur Fachlichkeit
Value Objects …
‣ #1: sind Objekte und keine Primitive
‣ #2: sind unveränderbar
‣ #3: konvertieren fachlich
‣ #4: normalisieren fachlich
‣ #5: validieren fachlich
‣ #6: sind vollwertig und vergleichbar
Java
Land
offenkundiggut #WISSENTEILEN
Value Objects
42. offenkundiggut #WISSENTEILEN
/**
* #1: Value Objects sind Objekte
*/
public class Customer {
private Date birthDate;
...
)
Mut zur Fachlichkeit
Value ObjectsJava
Land
43. offenkundiggut #WISSENTEILEN
/**
* #1: Value Objects sind Objekte
*/
public class Customer {
private DateOfBirth birthDate;
...
)
Mut zur Fachlichkeit
Value ObjectsJava
Land
44. offenkundiggut #WISSENTEILEN
/**
* Represents a date of birth
*/
public class DateOfBirth {
private Date dateOfBirth;
public boolean isBirthDayToday() {
return ...;
}
public int getCurrentAge() {
return ...;
}
}
Mut zur Fachlichkeit
Value ObjectsJava
Land
45. offenkundiggut #WISSENTEILEN
/**
* #2: Value Objects sind unveränderbar
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) { ... }
public Date getDate() {
return new Date(dateOfBirth.getTime());
}
}
Immutable!
Mut zur Fachlichkeit
Value ObjectsJava
Land
46. offenkundiggut #WISSENTEILEN
/**
* #3: Value Objects konvertieren fachlich
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) { ... }
public DateOfBirth(String date) { ... }
public DateOfBirth(String date,
String pattern) { ... }
...
}
Konvertieren!
Mut zur Fachlichkeit
Value ObjectsJava
Land
47. offenkundiggut #WISSENTEILEN
/**
* #4: Value Objects normalisieren fachlich
*/
public class DateOfBirth {
private Date dateOfBirth;
public DateOfBirth(Date date) {
...;
this.dateOfBirth = copyAndStripTimeStamp(date);
}
...
}
Normalisieren!
Mut zur Fachlichkeit
Value ObjectsJava
Land
48. offenkundiggut #WISSENTEILEN
/**
* #5: Value Objects validieren fachlich
*/
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);
} ...
} ...
}
Validieren!
Mut zur Fachlichkeit
Value ObjectsJava
Land
49. offenkundiggut #WISSENTEILEN
/**
* #6: Value Objects sind vollwertig
*/
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 ...;
}
}
Full blown Object!
Mut zur Fachlichkeit
Value ObjectsJava
Land
50. Mut zur Fachlichkeit
Value Objects Beispiele
‣ FirstName, LastName, DateOfBirth
‣ ZipCode, City, Country
‣ Email, PhoneNumber
‣ Note, Description
‣ Year, Month, …
‣ Money, …
Java
Land
offenkundiggut #WISSENTEILEN
Value Objects
51. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Value Objects, wo sind da die Grenzen
‣ Ein Value Object repräsentiert eine fachlich
relevante Eigenschaft des Domain Models.
Value ObjectsJava
Land
52. Mut zur Fachlichkeit
offenkundiggut #WISSENTEILEN
Value Objects, wo sind da die Grenzen
‣ Ein Value Object repräsentiert eine fachlich
relevante Eigenschaft des Domain Models.
Value ObjectsJava
Land
53. Mut zur Fachlichkeit
Rich Domain Model Benefits
‣ Fachlichkeit befindet sich direkt in der Domain
‣ Kein/kaum duplizierter Code
‣ sprechender & konsistenter Code
‣ verbesserter Lesbarkeit & Wartbarkeit
‣ Keine Util, Helper, Manager & Friends
Java
Land
offenkundiggut #WISSENTEILEN
Putting together
54. offenkundiggut #WISSENTEILEN
Mut zur Fachlichkeit
// Entity with Value Objects
public class Customer {
private FirstName firstName;
private LastName lastName;
private FamilyStatus familyStatus;
private Address postalAddress;
private Map<PhoneNumberType, PhoneNumber> phoneNumbers;
private Map<EmailType, Email> emailAddresses;
...
public void marry(LastName newLastName) {
lastName = newLastName
familyStatus = FamilyStatus.MARRIED;
}
public boolean isMarried() {
return (familyStatus == FamilyStatus.MARRIED);
}
}
Putting togetherJava
Land
55. offenkundiggut #WISSENTEILEN
Mut zur Fachlichkeit
// Entity with Value Objects
public class Customer {
...
private Map<PhoneNumberType, PhoneNumber> phoneNumbers;
...
public void changeHomePhoneNumber(
PhoneNumber changedHomePhoneNumber) { ... }
public void disconnectHomePhone() { ...}
public void changeMobilePhoneNumber(
PhoneNumber changedHomePhoneNumber) { ... }
public void disconnectMobilePhone() { ... }
...
}
Putting togetherJava
Land
57. Mut zur Fachlichkeit
Das kleine 1x1 des Builder Pattern
‣ Object Creation Design Pattern
‣ Antwort auf das Constructor-Hell Anti-Pattern
‣ Step-by-Step Ansatz
‣ Kandidat für Fluent-API
Java
Land
offenkundiggut #WISSENTEILEN
Builder Pattern
58. // 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
...;
}
Mut zur Fachlichkeit
Schön? Nee!
Java
Land
offenkundiggut #WISSENTEILEN
Builder Pattern
59. // 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();
Mut zur Fachlichkeit
Schöner?
Java
Land
offenkundiggut #WISSENTEILEN
Builder Pattern
60. 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;
}
...
}
Mut zur Fachlichkeit
Naja!
Java
Land
Builder Pattern
61. // Builder Pattern - Extended Edition IN ACTION
// create AddressBuilder
Address address = Address.newAddress()
// set values
.forStreetNumber(...).ofStreet(...)
.inCity(...).withZipCode(...)
.lyingInState(...).ofCountry(...)
// build Address object
.build(...);
Mut zur Fachlichkeit
Schöner?
Java
Land
offenkundiggut #WISSENTEILEN
Builder Pattern
62. // Builder Pattern - Extended Edition with Inner Class Builder
public class Address {
public static Builder newAddress() {
return new Address().new Builder();
}
private String street;
public class Builder {
public Builder ofStreet(String initialStreet) {
street = validateStreet(initialStreet);
return this;
}
public Address build() {
validate();
return Address.this;
}
}
}
Mut zur Fachlichkeit
Ja!
Java
Land
offenkundiggut #WISSENTEILEN
Builder Pattern