Sympriser

Synergising the Enterprise through Innovation & IT

Archive for the ‘DDD’ Category

Revisiting Fowler’s Video Store: Refactoring Code, Refining Abstractions

with 2 comments

Read this article from our new blog at http://blog.symprise.net.

Refactoring Book

Refactoring techniques and Design Patterns have been extensively propagated and advocated for over a decade now. Why we still find hard to change, anaemic or overly complex object oriented structures? What is missing in our design practices?

In this series, we use Fowler’s didactic Video Store program to show practical advanced refactoring strategies that improve design simplicity, expressiveness and flexibility beyond standard object-oriented practice.

Refactoring and Design Patterns

Refactoring is a modularity improving technique that is essential for any evolutionary software development process and indispensable in the skill set of any professional software developer.

In the extremely influential book “Refactoring: Improving the Design of Existing Code”, Martin Fowler defines Refactoring as “a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour”.

Fowler’s Refactoring book and the GoF Design Patterns book have played a fundamental transformational role on the professional community, educating a generation of software developers on their move from the procedural to the object oriented design paradigm. More recently, Joshua Kerievsky published Refactoring to Patterns, leading refactoring to a new level of abstraction by explicitly combining both approaches.

Despite all these resources, why we still see complex object oriented structures, contrary to the objectives of simplicity, expressiveness and flexibility, also implied by the refactoring definition?

What is Missing?

High quality software designs are made of modular and expressive abstractions.

Refactoring and design patterns are misused by a large number of software developers. There is an excessive focus at the syntactical level. A broader and deeper perspective is needed when analysing modularity problems and directing the refactoring process towards greater relevance to its design objectives.

The missing points are:

  1. each refactoring step is an opportunity for refining abstractions;
  2. abstraction refinements should improve both modularity and expressiveness.

In summary: refactoring processes should consider the program’s context and domain semantics to explicitly rethink both its design structure and abstractions at each refactoring step.

Practical Strategies for Effective Design Improvement

Effective refactoring transcends the program’s text and syntax to improve modularity and keenly enhance simplicity, expressiveness and flexibility.

Fowler’s book introduces refactoring techniques with a small program for a video store, whose main function is to print a statement of a customer’s charges and his frequent renter points.

In this series, we use Fowler’s very simple and rich didactic example to illustrate the following refactoring strategies:

  1. Adopt Object-Oriented Modules. Apply proper object-oriented design to reduce syntactical noise and make programs easier and safer to work with. Prepare your program to implement most new requirements by small and focused interventions, without generating unintended side effects.
  2. Reveal the Relevant Domain Concepts. Listen carefully the domain experts to reduce semantic noise by making their relevant concepts explicit and evident in all design expressions.
  3. Align with Contextual Variance. Confront program invariants with contextual variances to help make design decisions that improve consistency and flexibility.
  4. Build Resonant Abstractions.

These refactoring strategies have been consistently beneficial to our software design practice over the years. In case you haven’t yet, we strongly suggest you try them in your daily software development activities. We will be very happy to listen to your feedback with ideas and experience about software design and refactoring.This blog moved to http://blog.symprise.net.

Written by Igor Subasic de Azevedo

April 28, 2009 at 9:42 am

Revisiting Fowler’s Video Store: Variants and Invariants

leave a comment »

Read this article from our new blog at http://blog.symprise.net.

Glove

Glove: supple and protective

“Nothing endures but change.”

Heraclitus (540-480 BC)

“A well-worn glove becomes supple at the points where the fingers bend, while other parts are stiff and protective.”

Domain-Driven Design, by Eric Evans

In our design and refactoring efforts, we can’t afford to join the epistemological quest for Heraclitus’ supreme Logos – the deep rational principle that rules all changes in the universe.

We can and should, however, reason about how change affects consistency in our software designs. For stateful programs, such as object-oriented software, this necessarily brings us to understand its variants and invariants and how they are translated to design flexibility and coherence in the face of contextual variance.

Our objective is to reflect on how to design software that fits our domain like a well-worn glove. Simple, flexible and durable. Easy to work with. Capable of bending without breaking where we most need. Consistent and adaptive in the face of change.

Program’s Invariants in the Face of Contextual Variance

An invariant is a logical expression that remains true throughout a sequence of transformations. Invariant analysis is traditionally employed for software design on a textual basis. Invariants are defined or identified and then verified for representation structures, iterations, objects and classes.  Here, we want to go beyond what is present in the software texts (the programs themselves).

This post resumes the Revisiting Fowler’s Video Store series. After making the relevant domain concepts explicit, we focus on another aspect of domain semantics, studying contextual variance under the perspective of the following issues:

  1. The Passage of Time;
  2. Changes in the Video Classification;
  3. Changes in Rental Prices and Terms.

The Passage of Time

The first variance in the context of any software system is the inexorable passage of time. In our case, it affects directly the video store rental periods, which are represented by a record of the number of days a video has been rented. This representation is not persistent: it needs to be incremented at the beginning of each business day, otherwise it would become invalid. Therefore, the representation is not consistent because it is incoherent with the program’s context.

We propose to replace the number of days by the initial rental date and to calculate the rental period through date arithmetic. The refactoring is internal to the Rental class, so there is no need to change its specification (or published interface).

The consistent rental period calculation can be readily implemented using the new RentalCalendar class, that applies the time point pattern with day granularity specifically for video rental business calendar. The alternative Rental class constructors and getDaysRented method are shown in the figure below.

public Rental(Video video, RentalCalendar initialDate) {

    _video = video;
    _initialDate = initialDate;
}

public Rental(Video video, int daysRented) {

    _video = video;
    _initialDate = RentalCalendar.pastDay(daysRented);
}

public int getDaysRented() {

    return _initialDate.daysTo(RentalCalendar.today());
}

Two additional reflections on this refactoring:

  1. the initial rental date is another relevant domain concept that we made explicit in our program;
  2. it confirms again an obvious truth: we should only persist what is persistent.

Changes in the Video Classification

Both the initial Video Store dicdatic program and its purely syntactical refactoring use a static classification scheme for pricing information. In the initial program, each video is  associated with a price code; purely syntactical refactoring replaces this association with an association to a price subclass.

There is an apparent contradiction in this model that should be questioned and clarified:

“Do New Releases stay in this category for ever?”

If the domain expert answers “No. Of course not.”, then the next question should be:

“If a video is reclassified, how does it affect the calculations for its current rentals?”

Programs must be perfectly adherent to all relevant business rules. If the domain experts affirm the calculations should remain the same as when the customer rented the video, we should transfer the pricing information from the  Video class to the Rental class, because it is transient to Video and persistent to Rental.

In the previous post, we introduced the Conditions class, created to represent the domain concepts that regulate the price and frequent renter points. Now we just need to adapt the Rental class constructors and its getCharge method, as shown in the following program fragment.

public Rental(Video video, RentalCalendar initialDate, Conditions conditions) {

    _video       = video;
    _initialDate = initialDate;
    _conditions  = conditions;
}

public Rental(Video video, int daysRented, Conditions conditions) {

    _video       = video;
    _initialDate = RentalCalendar.pastDay(daysRented);
    _conditions  = conditions;
}

public double getCharge() {

    return _conditions.getCharge(this.getDaysRented());
}

Changes in Rental Prices and Terms

I enjoy watching a video every week at home. I usually rent two or three for my family at my local video store. This week I saw something that made me rent seven videos! Look yourself at the picture below.

Simultaneous Rentals Promotion

Simultaneous Rentals Promotion

I think the store owner finally understood my needs and the result is more business and satisfaction. For us, here, this is a good example of change in rental prices and terms. For regular and children’s videos, the rental conditions now depend on the number of simultaneous rentals. Each additional video rent extends the rental term by one day. So, the initial term is 3 days for 3 videos, 4 days for 4 videos and so on up to 7 videos.

In the original didactic program the prices and terms of rental for each type of video are expressed as literals embedded in the calculation formulas. Standard object-oriented design practice refactors the conditional expressions into a specialisation solution based on inheritance and polymorphism that implements the strategy design pattern through a class hierarchy.

We replaced the conditional expressions by parameterised unified expressions, where the parameters are domain relevant concepts, originally implicit and then explicitly articulated as rental conditions. The program fragment below shows how easily the rental conditions value object can be moulded to face the changes in rental terms.

public Collection<Rental>
simultaneousPromotion (Collection<Video> videoCollection, RentalCalendar initialDate) {

    int simultaneous = videoCollection.size();
    List<Rental> rentalCollection = new LinkedList<Rental>();

    if (simultaneous >= 3 && simultaneous <= 7) {

        int initialTerm = simultaneous;
        Conditions conditions = new Conditions(initialTerm, 3.00, 1.50, 1);

        for (Video eachVideo : videoCollection) {

            Rental rental = new Rental(eachVideo, initialDate, conditions);
            rentalCollection.add(rental);
        }
    }

    return rentalCollection;
}

As we can see, there was no need to change the calculation logic. The refactoring solution based on making relevant concepts explicit is not only simpler and more expressive, but also and more flexible and consistent in the face of contextual variance.

Conclusions

Confronting the program invariants with relevant contextual variances illuminates the refactoring process. As usual, we should always listen the domain experts, strive for simplicity and use a maintenance need as an opportunity for refactoring.

In this post, we brought contextual variance to our refactoring process. Working through the program invariants in the face of contextual variance generated two contributions for our refactoring of the Video Store program:

  1. it made apparent the need to refactor the representation for the rental periods;
  2. it confirmed the value object based refactoring of the program’s conditional expressions.

Refactoring for coherence with relevant contextual variances improves the software design in terms of consistency and flexibility. Just like an well worn glove, our refactored program is now more consistent and flexible exactly where it makes most sense.

Design coherence at the syntactical level is achieved by colocating data and functions responsibilities. At semantic level, coherence means consistency and flexibility in the face of domain dynamics. This requires deeper thinking about the relevant contextual variance and the relevant domain concepts.

Written by Igor Subasic de Azevedo

April 24, 2009 at 1:21 pm

Revisiting Fowler’s Video Store: Making Implicit Concepts Explicit

with 5 comments

Read this article from our new blog at http://blog.symprise.net.

It's not just a Gear, it is a Clock!

Not just a Gear, a Clock!

“A deep model contains the central concepts and abstractions that can succinctly and flexibly express essential knowledge of the users’ activities, their problems and their solutions.”

“Ultimately … this should make the software more in tune with the way the domain experts think and more responsive to the users’ needs”

Domain-Driven Design, by Eric Evans

Domain-Driven Design situates the domain model in a central and convergent role, tightly articulating semantics and syntax for problem definition, solution design and software implementation.

In this post, we bring the domain semantics to our refactoring process. We want a deeper perspective to analyse modularity problems and to direct improvements towards greater relevance to our design objectives. So, let’s resume the Fowler’s Video Store refactoring and explore alternative design decisions based on greater emphasis on model semantics.

Fowler’s Video Store didactic program has two main functions: rental calculations and customer statement printing. This post focuses on the domain concepts that are most relevant to the rental calculations. Next posts will explore additional design alternatives for rental calculations and customer statement printing.

Initial Rental Calculations Design

Rental calculations include charge and frequent renter points. Both calculations are a function of the rental period and whether the video is either a new release, for children or regular.

The initial program defines a price code attribute for each video; this information is used to select the right calculation formulas embedded in switch programming statements inside the customer statement method. The complexity of such design grows as new price codes are added. It becomes specially dangerous if the statement method is duplicated to implement the HTML format.

Refactoring with Inheritance and Polymorphism

As we saw previously, the refactoring book evolves this design solution first by extracting the calculations to the methods getCharge and getPoints and moving them to the Video class.  This eliminates the need of duplicating the formulas when creating a new statement method.

The next step is to isolate the formulas from the Video class, replacing the price code with a new Price class. This class is then further specialised into the NewRelease, Children or Regular subclasses, so that the price code can be replaced by a polymorphic Price reference to an object of the respective subclass.

The following figure shows the design solution adopted by the refactoring book.

Video Store - Price Polymorphism

Video Store - Price Specialisation

The recommended refactoring improves modularity by using inheritance and polymorphism to adopt a typical object oriented syntax, that implements the strategy design pattern. The refactoring steps end up constituting the Replace Conditional Logic with Strategy refactoring pattern.

It looks like the right way to go. Doesn’t it? Well, let’s explore a design alternative.

Finding Relevant Implicit Concepts

The rental model for prices and terms is implicitly codified in the following formulas scattered along the Price class hierarchy implementation:

regularCharge = 2.0;
if (daysRented > 2) regularPrice += (daysRented - 2) * 1.5;

childrensCharge = 1.5;
if (daysRented > 3) childrensCharge += (daysRented - 3) * 1.5;

newReleaseCharge = daysRented * 3;

If we look closely enough at these formulas, we discover they are actually three instances of the same formula:

charge = initialPrice;
if (daysRented > initialTerm)
  charge += (daysRented - initialTerm) * extraPricePerDay;

The implicit concepts of initial term, initial price and extra price per day have just emerged as relevant to our design! They define a clearer, explicit, flexible and unified pricing model, replacing three static functions by a single meaningfully parameterised function. The same is true for the frequent renter points calculations. The emerged concepts can be articulated as rental conditions.

Naturally, all relevant domain abstractions must be institutionalised as part of the language effectively practised in the business and expressed in the software. Therefore, emerging concepts must be presented, discussed, reviewed where necessary and validated with the business domain experts.

Making the Relevant Concepts Explicit in the Design

As part of this this convergence process, we propose to explicitly define the Conditions value object as a substitute for the Price class hierarchy. The parameters initial term, initial price, extra price and extra points are invariant for regular, children’s and new releases. Therefore they can be defined at construction time:

Conditions regular    = new Conditions(2, 2.0, 1.5, 0);
Conditions childrens  = new Conditions(3, 1.5, 1.5, 0);
Conditions newRelease = new Conditions(1, 3.0, 3.0, 1);

The parameter daysRented is defined at calling time for the functions getCharge and getPoints.

public class Conditions {

  private final int    _initialTerm;
  private final double _initialPrice;
  private final double _extraPricePerDay;
  private final int    _INITIAL_POINTS = 1;
  private final int    _extraPoints;

  public Conditions (int initialTerm, double initialPrice, double extraPricePerDay, int extraPoints) {

    _initialTerm      = initialTerm;
    _initialPrice     = initialPrice;
    _extraPricePerDay = extraPricePerDay;
    _extraPoints      = extraPoints;
}

  public double getCharge (int daysRented) {

    double charge = initialPrice;
    if (daysRented > initialTerm) charge += (daysRented - initialTerm) * extraPricePerDay;
    return (charge);
  }

  public int getPoints (int daysRented) {

    int points = _INITIAL_POINTS;
    if (daysRented > initialTerm) points += _extraPoints;
    return (points);
  }
}

After all changes the new domain model implementation is:

Video Store - Rental Conditions

Video Store - Rental Conditions

Conclusions

Refactoring at the semantic level improves the matching between business, model and implementation, adds flexibility where needed and addresses the major stakeholders’ concerns. As usual, we should always listen the domain experts, strive for simplicity and use a maintenance need as an opportunity for refactoring.

The initial program calculates rental charges and frequent renter points as conditional expressions. Standard object oriented design practice focuses on the conditions to specialise the expressions and implement the strategy design pattern for each calculation. This post focuses instead on the expressions themselves to identify the relevant domain concepts that generalise the expressions.

Bringing domain semantics to the process enabled us to propose a Extract Value Object refactoring, as an alternative to the Replace Conditional Logic with Strategy refactoring, with the following benefits:

  • decoupling: duplication in the multiple formulas is eliminated;
  • expressiveness: changes can be more clearly and easily applied;
  • simplicity: the program’s design structure is simplified.

Programs that instantiate an Anaemic Domain Model can be greatly improved by simple and straightforward refactoring aimed at adopting proper object oriented syntax. Additional improvements, however, require deeper thinking and carefully considering the domain model semantics in order to select the course of action from multiple design options.

Picture by Readerwalker’s photo stream. Original at Flickr.

Written by Igor Subasic de Azevedo

March 31, 2009 at 10:39 am

Follow

Get every new post delivered to your Inbox.