clean systems - github pages · dependency injection • special type of inversion of control. •...
TRANSCRIPT
Clean SystemsClean Code at the Architecture Level
Dean [email protected]’s, Twitter: deanwampler
1Wednesday, August 6, 2008
2Wednesday, August 6, 2008
How would you build a city?
3Wednesday, August 6, 2008
4Wednesday, August 6, 2008
5Wednesday, August 6, 2008
Cities are modular
• They have appropriate levels of abstraction.
• They separate concerns.
6Wednesday, August 6, 2008
Your software systems?
• Appropriate abstractions?
• Clear separation of concerns?
7Wednesday, August 6, 2008
Clean systems are built on clean code
Leave now if code makes you squeamish...
8Wednesday, August 6, 2008
Separate construction
fromuse
#1
9Wednesday, August 6, 2008
During construction
• People in hard hats.
• Lots of heavy lifting.
• ...
10Wednesday, August 6, 2008
During use
• People in nicer clothes.
• Business tasks.
• ...
11Wednesday, August 6, 2008
Software construction vs. use
• Startup is one task.
• Component wiring.
• Running involves different tasks.
12Wednesday, August 6, 2008
public Service getService() { if (service == null) // Good enough default for most cases? service = new MyServiceImpl(...); return service; }
An example
Lazy Initialization Pattern
13Wednesday, August 6, 2008
public Service getService() { if (service == null) // Good enough default for most cases? service = new MyServiceImpl(...); return service; }
What’s Wrong with LI?“Wiring strategy” scattered and tangled.
Specific decisions hard coded.
14Wednesday, August 6, 2008
Other problems...
• Testing
• Must somehow set a mock for service before getService called.
• Must still compile with MyServiceImpl.
• Is MyServiceImpl really the best default?
• Breaks the Single Responsibility Principle
15Wednesday, August 6, 2008
Setup concern
• Requires a global strategy.
• Consistent approach.
• Modularized decisions.
16Wednesday, August 6, 2008
SolutionDependency Injection
17Wednesday, August 6, 2008
Dependency Injection
• Special type of Inversion of Control.
• Objects are given their dependencies
• Passive vs. Active
• Authoritative mechanism makes wiring decisions.
18Wednesday, August 6, 2008
Options
• Attribute “setters”.
• Constructor arguments.
• Object leaves constructor fully formed.
• Slightly better.
19Wednesday, August 6, 2008
Spring Framework<beans> <bean id="service" class="org.example.services.MyServiceImpl"/>
<bean id="clientOfService" class="org.example.app.ClientImpl" p:service-name= "service"/> ...</beans>
XmlBeanFactory bf = new XmlBeanFactory( new ClassPathResource("app.xml", getClass()));Client client = (Client) bf.getBean("client");
20Wednesday, August 6, 2008
Dependency Injection
• Separates construction from use.
• Decouples abstractions from implementations.
21Wednesday, August 6, 2008
Scale up systems
on demand
#2
22Wednesday, August 6, 2008
Small vs. Large Systems
23Wednesday, August 6, 2008
Common Myth:
Why didn’t you get the design
right the first time?
24Wednesday, August 6, 2008
Design Dilemma
• The perfect design for today’s system.
vs.
• The perfect design for tomorrow’s system.
25Wednesday, August 6, 2008
Agile methods
• Taught us to evolve the design to meet today’s needs,
• But keep it adaptable for tomorrow’s needs
• Without anticipatory design.
26Wednesday, August 6, 2008
Software is unique
• Unlike physical structures,
• We can change everything in software, even the architecture.
27Wednesday, August 6, 2008
Software is unique
• … but only if we keep it agile!
28Wednesday, August 6, 2008
How not to keep it agileEJB’s versions 1 and 2
29Wednesday, August 6, 2008
Enterprise Java Beans
• Forced tangling of concerns:
• Application logic mixed with
• Container life-cycle, etc.
• Persistence,
• etc.
30Wednesday, August 6, 2008
public interface BankLocal extends javax.ejb.EJBLocalObject { …
Example: Bank EJB
• Forced to subclass an EJB class.
• Can’t use application domain hierarchy.
• Tight coupling to container details.
31Wednesday, August 6, 2008
… String getCity() throws java.ejb.EJBException; void addAccount(AccountDTO dto) throws java.ejb.EJBException; …
Example: Bank EJB
• Tight coupling for methods, too.
32Wednesday, August 6, 2008
public abstract class Bank implements javax.ejb.EntityBean { …
EJB implementation
• Forced to implement EJB interface
33Wednesday, August 6, 2008
EJB implementation … public void addAccount(AccountDTO dto) { InitialContext ctx = new InitialContext(); AccountHomeLocal accountHome = ctx.lookup("AccountHomeLocal"); AccountLocal account = accountHome.create(dto); Collection accounts = getAccounts(); accounts.add(account); }
34Wednesday, August 6, 2008
EJB Implementation
… // Required container methods... public void ejbActivate() {} public void ejbPassivate() {} public void ejbLoad() {} public void ejbStore() {} public void ejbRemove() {} …
35Wednesday, August 6, 2008
But wait, there’s more!
• Several more classes and interfaces.
• Many more methods.
• XML to define
• Transactions,
• Persistence mapping,
• Security, ...
36Wednesday, August 6, 2008
Forget about:
• Reuse.
• Object orientation of your domain model.
• => Lot’s of duplication between EJB’s and POJO domain objects.
• Easy TDD.
• High productivity...
37Wednesday, August 6, 2008
But, not all was wrong
• Using XML to specify transactional, persistence, and security behaviors,
• Separated these concerns from code.
• EJBs anticipated Aspect-Oriented Programming.
38Wednesday, August 6, 2008
SolutionAspect-Oriented Programming
39Wednesday, August 6, 2008
public class BankAccount { private Money balance; public Money getBalance () {…} public void credit(Money amount) { balance += amount; } public void debit(Money amount) { balance -= amount; } …}
Clean Code
40Wednesday, August 6, 2008
However,
real applications need:
Transactions
Persistence
Security
public void credit(…) { … } public void debit(…) { … }
41Wednesday, August 6, 2008
public void credit(Money amount) throws ApplicationException { try { Money oldBalance = balance; beginTransaction(); balance += amount; persistChange(this); …
So credit becomes…
42Wednesday, August 6, 2008
catch (Throwable th) { logError(th); balance = oldBalance; throw new ApplicationException(th); } finally { endTransaction(); }}
43Wednesday, August 6, 2008
We’re mixing multiple domains, with fine-grained intersections.
Transactions
Persistence
Security
“Problem Domain”
“tangled” code
“scattered” logic44Wednesday, August 6, 2008
Objects alone don’t prevent tangling.
45Wednesday, August 6, 2008
Aspects restore modularity by encapsulating the intersections.
Transactions
Persistence
Security
TransactionAspect
PersistenceAspect
SecurityAspect
46Wednesday, August 6, 2008
AOP tool options
• AspectJ
• “Pure Java” Spring AOP or JBoss AOP
47Wednesday, August 6, 2008
public aspect PersistentBankAccount { pointcut stateChange(BankAccount ba): (call(* BankAccount.debit(..)) || call(* BankAccount.credit(..))) && this(ba);
after(BankAccount ba): stateChange(ba) { persistChange(ba); }}
AspectJ
48Wednesday, August 6, 2008
public aspect PersistentBankAccount { pointcut stateChange(BankAccount ba): (call(* BankAccount.debit(..)) || call(* BankAccount.credit(..))) && this(ba);
after(BankAccount ba): stateChange(ba) { persistChange(ba); }}
AspectJ“Class-like” construct
When state changes occur.
When and how to persist changes.
49Wednesday, August 6, 2008
Spring AOP<beans> <bean id="bankDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="me" />
<bean id="bankDataAccessObject" class="com.banking.persistence.BankDataAccessObject" p:dataSource-ref= "bankDataSource"/>
<bean id="bank" class="com.banking.model.Bank" p:dataAccessObject-ref= "bankDataAccessObject"/></beans>
50Wednesday, August 6, 2008
: appDataSource
: bankDataAccessObject
: bank
getAccounts(): client
“Matryoshka” doll*
* i.e., Russian nested doll51Wednesday, August 6, 2008
EJB version 3
• Largely adopted the POJO model of Spring AOP.
52Wednesday, August 6, 2008
Aspect-Oriented Programming
• Separates concerns with fine-grained coupling.
• Allows concerns to evolve and scale independently.
• Allows architectures to evolve and scale.
53Wednesday, August 6, 2008
Test drivethe
system architecture
#3
54Wednesday, August 6, 2008
Cities are modular
• Discrete components.
• Minimal coupling.
• Concurrent modifications.
• Concurrent execution.
55Wednesday, August 6, 2008
They grow from villages
• Dirt roads are replaced by paved roads.
• Highways are added.
• Small buildings are replaced with towers.
The transition can be painful at times.
56Wednesday, August 6, 2008
Big Design Up Front?
• Architecture evolution is possible if,
• The components that implement concerns are decoupled from one another and
• The components are wired together using aspect-like mechanisms.
57Wednesday, August 6, 2008
Hazards of BDUF
• You’re thinking in a vacuum, without feedback from a running system.
• It’s hard to throw the design away when you’ve invested so much into it.
58Wednesday, August 6, 2008
SolutionTest-Driven Development
59Wednesday, August 6, 2008
In TDD, tests are proxies for
requirements
60Wednesday, August 6, 2008
Therefore, grow the system in response to
“test pressure”
61Wednesday, August 6, 2008
Optimizedecision making
#4
62Wednesday, August 6, 2008
The best decisions:
• are made at the last responsible moment,
• when you have the most recent information.
63Wednesday, August 6, 2008
SolutionIncremental Evolution
64Wednesday, August 6, 2008
With a test-drivenarchitecture,
you can optimize decision making
65Wednesday, August 6, 2008
Timing decisions
• You can make
• many small decisions,
• rather than big, risky decisions.
66Wednesday, August 6, 2008
This is only possible with an agile architecture
67Wednesday, August 6, 2008
Use standardswisely,
when they add demonstrable value
#5
68Wednesday, August 6, 2008
Benefits of Standards
• Reuse and encapsulation of
• ideas.
• components.
• Shared expertise.
69Wednesday, August 6, 2008
Drawbacks of Standards
• Slow to emerge.
• Design by committee.
• Bloat.
70Wednesday, August 6, 2008
Does the standardmeet the needs it was
intended to serve?
71Wednesday, August 6, 2008
Minimize themental gap between
requirements and code
#6
72Wednesday, August 6, 2008
Does your coderead like the
problem domain?
73Wednesday, August 6, 2008
SolutionDomain-Specific Languages
74Wednesday, August 6, 2008
Every Domain has a Language
• Rich vocabulary.
• Idioms and patterns.
• Clear and concise communications.
75Wednesday, August 6, 2008
Code should read like the domain
• DSL’s
• Introduce appropriate levels of abstraction.
• Minimize mental gap between domain concepts and code.
• Optimize communication.
76Wednesday, August 6, 2008
Recap
• Separate construction from use.
• Use dependency injection.
• Scale up on demand.
• Decouple concerns with Aspects.
77Wednesday, August 6, 2008
Recap
• Test-drive the system architecture.
• Requires modular concerns.
• Optimize decision making.
• An agile architecture lets you make decisions at the most appropriate times.
78Wednesday, August 6, 2008
Recap
• Use standards wisely.
• Only if they demonstrate value.
• Use domain-specific languages.
• Map the domain to code.
• Introduce appropriate levels of abstraction.
79Wednesday, August 6, 2008
Final Thought:
Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test. ...
Each of us should ... explore and embrace techniques to reduce complexity.
Ray Ozzie, Chief Technology Officer, Microsoft Corporation
80Wednesday, August 6, 2008
Thank You!
• http://blog.objectmentor.com
• http://aspectprogramming.com/papers
81Wednesday, August 6, 2008