relentless refactoring

45
Relentless Refactoring Strategic improvement through Domain-Driven Design Mark Rickerby

Upload: mark-rickerby

Post on 13-Jan-2015

1.185 views

Category:

Technology


1 download

DESCRIPTION

 

TRANSCRIPT

Relentless RefactoringStrategic improvement through 

Domain-Driven Design

Mark Rickerby

@maetlmaetl.netgithub.com/maetl

Introducing the most popular software architecture in the world...

¶ Big Ballof Mud

The year 2000 in web softwareContinuum of architectural confusion

brutalist structure

organic miasma

procedural PHP, Perl, ASP, etc...

spaghetti code jungle 

J2EE frameworkstandards

indirection and bloat

The year 2000 in web softwareContinuum of architectural confusion

brutalist structure

organic miasma

“Elegance is not a dispensable luxury but a

factor that decides between success and

failure.”

Edsger Dijkstra

Design patternsEventually developers figured it out

Design patternsAn explosion of web frameworks followed

Convention over configurationGeneric Model-View-Controller separation

app/controllers/CategoriesController.phpapp/controllers/ProductsController.phpapp/models/Category.phpapp/models/Product.phpapp/templates/categories/*app/templates/products/*

What is the model?Are these data objects or domain objects?

app/controllers/CategoriesController.phpapp/controllers/ProductsController.phpapp/models/Category.phpapp/models/Product.phpapp/templates/categories/*app/templates/products/*

Anemic modelsThe result of leaky abstractions

— the app/models convention is a sensible default for basic CMS driven websites

— Active Record fools developers into thinking that persistence is the core concept of the ‘M’ in MVC

Eventual inconsistencyIf a shortcut exists it will be used

— when the software model does not capture rich relationships and behavior, controllers and templates expedite the process of adding new functionality

Eventual inconsistencyFramework defaults become inexact

app/controllers/CategoriesController.phpapp/controllers/CheckoutController.phpapp/controllers/ProductsController.phpapp/models/Category.phpapp/models/Product.phpapp/templates/categories/*app/templates/checkout/*app/templates/products/*

Ad-hoc developmentWhen features outgrow the framework

— when in doubt, developers add new behavior to existing methods

— bug-fixes and knowledge about system behavior accumulates in procedural code

Metaphor shearLoss of cohesion; design disorientation

Ad-hoc duplicationReliance on utility functions for common tasks

$units = $config->defaultWeightUnits;$w = explode(' ', $product->weight);

if ($w[1] != 'lbs') {   $weight = convertWeight(                $w[0], $units, 'lbs'              );} else {   $weight = $w[0];}

 

Ad-hoc behaviourIndependent services tangled inside existing interface

class Product extends ActiveRecord {

  public function create($imported=false) {     if ($imported) {      // edge case when the      // product is created from      // a bulk import    }  }   }

¶ Domain Driven Design

Domain Driven DesignEric Evans, 2004

really a book about refactoring

Discover missing conceptsRefactoring towards deeper insight

— focus on conceptual integrity

— be prepared to look at things in a different way

— constantly refine the language used to describe the software domain

A unified languageNot just a collection of singular entities

$ mv app/models app/model

 

A unified languageBuilding blocks for a fine grained model

Entity~ persistent ‘noun’ with identity and stateeg: product, category

Value~ atomic object with no unique identityeg: price, currency, weight

Service~ stateless ‘verb’, action or process

Aggregate~ a cluster of related objects

Ad-hoc duplicationReliance on utility functions for common tasks

$units = $config->defaultWeightUnits;$w = explode(' ', $product->weight);

if ($w[1] != 'lbs') {   $weight = convertWeight(                $w[0], $units, 'lbs'              );} else {   $weight = $w[0];}

Small values Immutable objects that model basic domain concepts

app/model/Product.phpapp/model/Weight.php

$weight = $product->weight->lbs();

 

Ad-hoc behaviourIndependent services tangled inside existing interface

class Product extends ActiveRecord {

   public function create($imported=false)   {      if ($imported) {       // edge case when the       // product is created from       // a bulk import     }   }     } 

Service layerBreak out standalone tasks into transactional interface

class ProductImporter extends BulkImporter {      public function importProduct($product)   {      // no longer an edge case   }}

Aggregate rootsDivide complex entities into a more granular model

app/model/Attribute.phpapp/model/AttributeValue.phpapp/model/Product.phpapp/model/ProductAttribute.phpapp/model/ProductDetails.phpapp/model/Sku.php

class Product extends Aggregate {}

Benefits of a richer modelWhy use object oriented design anyway?

— easier to localize changes when behavior and data are close together

— negotiate complexity with precise language

— objects have a single responsibility; easier to understand and maintain

Design is continuousChange code based on improved understanding

— models are dynamic;always changing and growing

— models are explanatory;define domain knowledge in code

AmbiguitiesContradictory concepts in the domain language

— avoid trying to build a monolithic model that captures every aspect of the domain

— tackle ambiguity by grouping the model into Bounded Contexts

Model namespaceGrouping based on pattern types

app/model/collections/ProductList.phpapp/model/entities/Product.phpapp/model/services/ShippingProvider.phpapp/model/services/TaxRate.phpapp/model/values/Weight.phpapp/model/values/Price.php

 

Model namespaceGrouping based on context boundaries

app/model/Invoicing/TaxRate.phpapp/model/Shipping/Provider.phpapp/model/Storefront/Price.phpapp/model/Storefront/Product.phpapp/model/Storefront/ProductList.phpapp/model/Storefront/Weight.php

 

Shared coreExtract common behaviour into a shared context

app/model/Core/Price.phpapp/model/Core/Weight.phpapp/model/Core/Product.phpapp/model/Invoicing/TaxRate.phpapp/model/Shipping/Provider.phpapp/model/Storefront/ProductList.php

Context mapDefine relationships between bounded contexts

HomonymsOverloaded concepts in the domain language

— the same word is used to describe many different concepts

— the same concept has many different responsibilities

Bounded contextsAllow ambiguous concepts to co-exist

app/model/Core/Price.phpapp/model/Core/Weight.phpapp/model/Core/Product.phpapp/model/Invoicing/TaxRate.phpapp/model/Shipping/Product.phpapp/model/Shipping/Provider.phpapp/model/Storefront/Product.phpapp/model/Storefront/ProductList.php

Anticorruption layerIsolating adapter between incompatible contexts

Refactoring is designChanging the structure of existing code

Refactoring is designChanging the structure of component boundaries

Application boundariesRefactoring patterns for evolving architecture

Introduce Strangler~new abstraction that overtakes the host system like a strangling vine

Extract Service~spawn a separate application by extracting a service from the original system

SummaryRelentless improvement

Separate infrastructure from the model

Use precise language to define domain concepts

Model complex behaviour in bounded contexts

ThanksMark Rickerby, 2011