KEMBAR78
Architecting well-structured Java applications | PDF
ARCHITECTING WELL-STRUCTURED JAVA
APPLICATIONS
Eduards Sizovs
@eduardsi
MOST APPS BEGIN LIFE SMALL AND NEAT.
TIME GOES BY...
HELLO. I AM YOUR ROTTING ENTERPRISE APP.
- RIGIDITY
- FRAGILITY
- IMMOBILITY
- VISCOSITY
- OPACITY
- NEEDLESS COMPLEXITY
- NEEDLESS REPITITION
SMELLING SYMPTOMS ->
HIBERNATE CORE V4.3.8.FINAL
JDK V1.7.0_51
A JDK CODE BASE IS DEEPLY INTERCONNECTED AT BOTH THE API AND THE
IMPLEMENTATION LEVELS, HAVING BEEN BUILT OVER MANY YEARS
PRIMARILY IN THE STYLE OF A MONOLITHIC SOFTWARE SYSTEM. WE’VE
SPENT CONSIDERABLE EFFORT ELIMINATING OR AT LEAST SIMPLIFYING AS
MANY API AND IMPLEMENTATION DEPENDENCES AS POSSIBLE, SO THAT
BOTH THE PLATFORM AND ITS IMPLEMENTATIONS CAN BE PRESENTED AS A
COHERENT SET OF INTERDEPENDENT MODULES, BUT SOME PARTICULARLY
THORNY CASES REMAIN.
(C) MARK REINHOLDS, CHIEF ARCHITECT OF THE JAVA PLATFORM
SPRING V4.1.6
PRINCIPLES.
PACKAGE IS THE FIRST-CLASS CITIZEN AND
KEY ELEMENT OF LOGICAL DESIGN.
TREAT PACKAGES AS A HIERARCHY EVEN IF
THEY’RE REPRESENTED FLAT.
io.shwitter.user
io.shwitter.user.registration
io.shwitter.user.profile
io.shwitter.timeline
 
io.shwitter.user (part of)
io.shwitter.user.registration
io.shwitter.user.profile
io.shwitter.timeline
 
io.shwitter.user
io.shwitter.user.registration (part of)
io.shwitter.user.profile
io.shwitter.timeline
 
io.shwitter.user
io.shwitter.user.registration
io.shwitter.user.profile (part of)
io.shwitter.timeline
 
io.shwitter.user
io.shwitter.user.registration
io.shwitter.user.profile
io.shwitter.timeline (part of)
 
USE PACKAGES TO GROUP FUNCTIONALLY-
RELATED ARTIFACTS. DO NOT GROUP
ARTIFACTS THAT DO THE SAME THING, BUT
ARE DIFFERENT BY NATURE.
io.shwitter.controller
io.shwitter.dao
io.shwitter.domain
io.shwitter.services
io.shwitter.exceptions
io.shwitter.controller
io.shwitter.dao
io.shwitter.domain
io.shwitter.services
io.shwitter.exceptions
io.shwitter.user
io.shwitter.user.registration
io.shwitter.user.profile
io.shwitter.timeline
 
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- DECOUPLING / ABSTRACTING FOR INDEPENDENT EVOLUTION (HA-HA)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- DECOUPLING / ABSTRACTING FOR INDEPENDENT EVOLUTION (HA-HA)
- DECOUPLING FOR REUSE (HA-HA)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- DECOUPLING / ABSTRACTING FOR INDEPENDENT EVOLUTION (HA-HA)
- DECOUPLING FOR REUSE (HA-HA)
- SEPARATION OF CONCERNS (IS PARTICULAR LAYER OUR CONCERN?)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- DECOUPLING / ABSTRACTING FOR INDEPENDENT EVOLUTION (HA-HA)
- DECOUPLING FOR REUSE (HA-HA)
- SEPARATION OF CONCERNS (IS PARTICULAR LAYER OUR CONCERN?)
- RELATED STUFF CO-LOCATION (ARE DAOS REALLY RELATED?)
- ABILITY TO DISTRIBUTE YOUR LAYERS OVER MULTIPLE PHYSICAL TIERS (HA-HA)
- DECOUPLING / ABSTRACTING FOR EXHANGEABILITY (HA-HA)
- DECOUPLING / ABSTRACTING FOR INDEPENDENT EVOLUTION (HA-HA)
- DECOUPLING FOR REUSE (HA-HA)
- SEPARATION OF CONCERNS (IS PARTICULAR LAYER OUR CONCERN?)
- RELATED STUFF CO-LOCATION (ARE DAOS REALLY RELATED?)
- CONSTRAINT ENFORCEMENT (IS THERE A BETTER WAY?)
APPLY SERVICE-ORIENTED MINDSET TO
SOFTWARE STRUCTURE.
LAYERING IS YOUR SERVICE'S DETAIL AND IS
INTERNAL TO THE SERVICE.
GROUP TIGHTLY COUPLED CLASSES
TOGETHER. IF CLASSES THAT CHANGE
TOGETHER ARE IN THE SAME PACKAGE, THEN
THE IMPACT OF CHANGE IS LOCALIZED.
- THE COMMON CLOSURE PRINCIPLE
MAKE PACKAGES HIGHLY COHESIVE BY
FOLLOWING SINGLE RESPONSIBILITY
PRINCIPLE.
io.shwitter.user.registration
io.shwitter.user.notifications
io.shwitter.user.profile
io.shwitter.user.profile.blocking
io.shwitter.user.registration
io.shwitter.user.notifications
io.shwitter.user.profile
io.shwitter.user.profile.blocking
io.shwitter.user :(
 
KEEP PACKAGES LOOSELY COUPLED, IDEALLY
– COMPLETELY INDEPENDENT. REFLECTION
DOESN’T COUNT.
package io.shwitter.user
@Entity
class User {
// name, password etc.
}
package io.shwitter.timeline
@Entity
class Timeline {
@OneToOne User user;
}
package io.shwitter.user
@Embeddable
class UserId {
Long value;
}
package io.shwitter.timeline
@Entity
class Timeline {
@Embedded UserId userId;
}
PROVIDE SLIM PACKAGE INTERFACE AND HIDE
IMPLEMENTATION DETAILS.
AVOID DEPENDENCY MAGNETS. SOMETIMES
DUPLICATION IS NOT THAT EVIL.
MANAGE RELATIONSHIPS. EVERY
DEPENDENCY ARROW HAS A REASON.
FINDBUGS V1.0 - A GREAT START
FINDBUGS V1.1 – IMPERFECTION CREEPS IN
FINDBUGS V1.2 – IMPERFECTION TAKES HOLD
FINDBUGS V1.3 – CHAOS BEGINS
FINDBUGS V1.4 – EXPLOSION
THE DEPENDENCIES BETWEEN PACKAGES
MUST NOT FORM CYCLES. BURN BI-
DIRECTIONAL DEPENDENCES IN FIRE.
HOW?
MERGING
package io.shwitter.user
class User {
void register(RegistrationNotifier notifier) {}
}
package io.shwitter.user.notify
class RegistrationNotifier {
void notify(User user) {}
}
MERGING - REPACKAGING
package io.shwitter.user
class User {
void register(RegistrationNotifier notifier) {}
}
class RegistrationNotifier {
void notify(User user) {}
}
DEPENDENCY INVERSION
package io.shwitter.user
class User {
void register(RegistrationNotifier notifier) {}
}
package io.shwitter.user.notify
class RegistrationNotifier {
void notify(User user) {}
}
DEPENDENCY INVERSION - REFACTORING STEP 1
package io.shwitter.user
class User {
void register(RegistrationNotifier notifier) {}
}
package io.shwitter.user.notify
class RegistrationNotifier implements UserNotifier {
void notify(User user) {}
}
interface UserNotifier {
void notify(User user) {}
}
DEPENDENCY INVERSION - REFACTORING STEP 2
package io.shwitter.user
class User {
void register(UserNotifier notifier) {}
}
package io.shwitter.user.notify
class RegistrationNotifier implements UserNotifier {
void notify(User user) {}
}
interface UserNotifier {
void notify(User user) {}
}
DEPENDENCY INVERSION - REFACTORING STEP 3
package io.shwitter.user
class User {
void register(UserNotifier notifier) {}
}
interface UserNotifier {
void notify(User user) {}
}
package io.shwitter.user.notify
class RegistrationNotifier implements UserNotifier {
void notify(User user) {}
}
ESCALATION
package io.shwitter.user
class User {
void register(Notifier n) { n.notify(this); }
}
package io.shwitter.user.notify
class Notifier {
void notify(User user) { sendEmailTo(user.email()); }
}
ESCALATION - REFACTORING STEP 1
package io.shwitter.user
class User {
void register(Notifier n) { n.notify(this); }
}
package io.shwitter.user.notify
class Notifier {
void notify(User user) { sendEmailTo(user.email()); }
}
package io.shwitter.user.registration
class Registrator {
void register(User user, Notifier n) {}
ESCALATION - REFACTORING STEP 2
package io.shwitter.user
class User {
void register() { }
}
package io.shwitter.user.notify
class Notifier {
void notify(User user) { sendEmailTo(user.email()); }
}
package io.shwitter.user.registration
class Registrator {
void register(User user, Notifier n) { n.notify(user); }
ESCALATION - REFACTORING STEP 3
package io.shwitter.user
class User {
void register() { }
}
package io.shwitter.user.notify
class Notifier {
void notify(String emailAddress) { sendEmailTo(emailAddress); }
}
package io.shwitter.user.registration
class Registrator {
void register(User user, Notifier n) { n.notify(user.email()); }
DEMOTION
package io.shwitter.user
class User {
void register(Notifier n) { n.notify(this); }
}
package io.shwitter.user.notify
class Notifier {
void notify(User user) { sendEmailTo(user.email()); }
}
DEMOTION - REFACTORING STEP 1
package io.shwitter.user
class User implements EmailHolder {
void register(Notifier n) { n.notify(this); }
}
package io.shwitter.user.notify
class Notifier {
void notify(User user) { sendEmailTo(user.email()); }
}
package io.shwitter.emailer
interface EmailHolder {
String email();
DEMOTION - REFACTORING STEP 2
package io.shwitter.user
class User implements EmailHolder {
void register(Notifier n) { n.notify(this); }
}
package io.shwitter.user.notify
class Notifier {
void notify(EmailHolder emailHolder) { sendEmailTo(emailHolder.email()
}
package io.shwitter.emailer
interface EmailHolder {
String email();
TOOLS
OO Design Principles & Metrics, Jason Gorman http://goo.gl/RTW9GT
The Economics of Software Design, J.B. Rainsberger http://goo.gl/ra7Q8Q
SOLID Principles, Eduards Sizovs http://goo.gl/Rpxavd
Designing Object-Oriented Software, Jouni Smed http://goo.gl/iyE1R2
Grand Unified Theory Of Software Design, Jim Weirich http://goo.gl/ASqyAs
Fun With Modules, Kirk Knoernschild http://goo.gl/i8jx8Y
Patterns of Modular Architecture http://goo.gl/yFqmZO
Let’s turn packages into a module system! http://goo.gl/Mzco8F
MORE
EITHER YOU WORK TO CREATE A
SOFTWARE STRUCTURE OR YOU DON'T.
EITHER WAY A STRUCTURE WILL EMERGE.
@EDUARDSI
THANK YOU
Architecting well-structured Java applications

Architecting well-structured Java applications