You are viewing the Articles in the Software Engineering Category

Domain Models and Value Objects

The other day a friend asked me what is the difference between a Value Object and a Domain Model, and when I would suggest using one over the other?

Since I have been asked this very same question quite a few times, I thought it might prove useful to provide a brief definition in the context of a language agnostic idiom which could serve as a point of reference for others as well. Thus, below is general definition of each.

Domain Models

A Domain Model is anything of significance which represents a specific business concept within a problem domain. Domain Models are simply classes which represent such concepts by defining all of the state, behavior, constraints and relationships to other Domain Models needed to do so. Essentially, a Domain Model “models” a domain concept, such as a Product, a User, or anything which could be defined within a problem domain itself, outside of the context of code.

Domain Models promote reuse and eliminate redundancy by defining specific classes which encapsulate business logic, state, behaviors and relationships. As business domain concepts change, so to do the implementations of the Domain Models.

Value Objects

As the name implies, a Value Object, more commonly referred to as a VO, is an object which simply provides values, nothing more.

Value Objects are entirely immutable; that is, all properties are read-only and assignments to those properties are specified only during object creation; after which, properties can not be modified and, by design, should not require changes.

Value Objects are typically used to provide an aggregation of conceptually related properties whose values describe the initial state of the object when instantiated and do not require any real concept of identity or uniqueness. While there are some edge cases (such as validation), more commonly than not, Value Objects do not implement any specific behavior. Conceptually, think of a Value Objects as being nothing more than an object which holds a value, or series of related values, which describe something about the object when created.

It is important to make the distinction between Value Objects and Domain Models, as a Value Objects is not a Model, but rather, it is nothing more than an object which holds values and could be used to describe any particular context. Perhaps a good example of a Value Object could be a JSON object returned from the server. That is a Value Object. A Domain Model could then wrap the Value Object in order to provide state changes, validation and behaviors.

And that’s it

Hopefully the above descriptions of both Domain Models and Value Objects will clear up any confusion surrounding the two concepts; ideally, making it easier to understand when to use each.

The point to keep in mind is that Domain Models simply model a business concept, including it’s rules, constraints and behaviors, while Value Objects simply describe a contextual state.

Misplaced Code

Often I come across what I like to call “Misplaced Code”, that is, code which should be refactored to a specific, independent concern rather than mistakenly being defined in an incorrect context.

For instance, consider the following example to get a better idea of what I mean:

Taking the above example into a broader context, it is quite common to see code such as this scattered throughout a codebase; particularly in the context of view concerns. At best this could become hard to maintain and, at worst, it will result in unexpected bugs down the road. In most cases (as in the above example) the actual code itself is not necessarily bad, however it is the context in which it is placed which is what I would like to highlight as it will almost certainly cause technical debt to some extent.

Considering the above example, should code such as this become redundantly implemented throughout a codebase it is quite easy to see how it can become a maintenance issue as, something as simple as a change to a hostname would require multiple refactorings. A much more appropriate solution would be to encapsulate this logic within a specific class whose purpose is to provide a facility from which this information can be determined. In this manner unnecessary redundancy would be eliminated (as well as risk) and valuable development time would be regained as the code would need only be tested and written once – in one place.

So again, using the above example, this could be refactored to a specific API and client code would leverage the API as in the following:

This may appear quite straightforward, however, I have seen examples (this one in particular) in numerous projects over the years and it is worth pointing out. Always take the context to which code is placed into consideration and you will reap the maintenance benefits in the long run.

Guiding Design with Behavior Verification and Mock Objects

At some point every developer who has disciplined themselves in the ritualistic like art and science of Test Driven Development soon discovers that the collaborators on which a class under test depend introduce an additional layer of complexity to consider when writing your tests – and designing your APIs.

For example, consider a simple test against a class Car which has an instance of class Engine. Car implements a start method which, when invoked, calls the Engine object’s run method. The challenge here lies in testing the dependency Car has on Engine, specifically, how one verifies that an invocation of Car.start results in the Engine object’s run method being called.

There are two ways of testing the above example of Car, which in unit testing nomenclature is called the System Under Test (SUT), and it’s Engine instance which is Car's Depended-on Component (DOC). The most common approach is to define assertions based on the state of both the SUT and it’s DOC after being exercised. This style of testing is commonly referred to as State Verification, and is typically the approach most developers initially use when writing tests.

Using the above Car example, a typical State Verification test would be implemented as follows:

Figure 1. CarTest, State Verification.

From a requirements perspective and therefore a testing and implementation perspective as well, the expectation of calling start on Car is that it will A.) change it’s running state to true, and B.) invoke run on it’s Engine instance. As you can see in Figure 1, in order to test the start method on Car the Engine object must also be tested. In the example, using the State Verification style of testing, Car exposes the Engine instance in order to allow the state of Engine to be verified. This has lead to a less than ideal design as it breaks encapsulation and violates Principle of Least Knowledge. Obviously, a better design of Car.isStarted could be implemented such that it determines if it’s Engine instance is also in a running state; however, realistically, Engine.run will likely need to do more than just set its running state to true; conceivable, it could need to do much, much more. More importantly, while testing Car one should only be concerned with the state and behavior of Car – and not that of its dependencies. As such, it soon becomes apparent that what really needs to be tested with regards to Engine in Car.start is that Engine.run is invoked, and nothing more.

With this in mind, the implementation details of Engine.run are decidedly of less concern when testing Car; in fact, a “real” concrete implementation of Engine need not even exist in order to test Car; only the contract between Car and Engine should be of concern. Therefore, State Verification alone is not sufficient for testing Car.start as, at best, this approach unnecessarily requires a real Engine implementation or, at worst, as illustrated in Figure 1, can negatively guide design as it would require exposing the DOC in order to verify its state; effectively breaking encapsulation and unnecessarily complicating implementation. To reiterate an important point: State Verification requires an implementation of Engine and, assuming Test First is being followed (ideally, it is), the concern when testing Car should be focused exclusively on Car and it’s interactions with its DOC; not on their specific implementations. And this is where the second style of testing – Behavior Verification – plays an important role in TDD.

The Behavior Verification style of testing relies on the use of Mock Objects in order to test the expectations of an SUT; that is, that the expected methods are called on it’s DOC with the expected parameters. Behavior Verification is most useful where State Verification alone would otherwise negatively influence design by requiring the implementation of needless state if only for the purpose of providing a more convenient means of testing. For example, many times an object may not need to be stateful or the behavior of an object may not always require a change in it’s state after exercising the SUT. In such cases, Behavior Verification with Mock Objects will lead to a simpler, more cohesive design as it requires careful design considerations of the SUT and it’s interactions with its DOC. A rather natural side-effect of this is promoting the use of interfaces over implementations as well as maintaining encapsulation.

For testing with Behavior Verification in Flex, there are numerous Mock Object frameworks available, all of which are quite good in their own right and more or less provide different implementations of the same fundamental concepts. To name just a few, in no particular order, there are asMock, mockito-flex, mockolate and mock4as.

While any of the above Mock Testing Frameworks will do, for the sake of simplicity I will demonstrate re-writing the Cartest using Behavior Verification based on mock4as – if for nothing other than the fact that it requires implementing the actual Mock, which helps illustrate how everything comes together. Moreover, the goal of this essay is to help developers understand the design concepts surrounding TDD with Behavior Verification and Mock Objects by focusing on the basic design concepts; not the implementation specifics of any individual Mock Framework.

Figure 2. CarTest, Behavior Verification approach.

Let’s go through what has changed in CarTest now that it leverages Behavior Verification. First, Car's constructor has been refactored to require an Engine object, which now implements an IEngine interface, which is defined as follows.

Figure 3. IEngine interface.

Note Engine.isRunning is no longer tested, or even defined as, it is simply not needed when testing Car: only the call to Engine.run is to be verified in the context of calling Car.start. Since focus is exclusively on the SUT, only the interactions between Car and Engine are of importance and should be defined. The goal is to focus on the testing of the SUT and not be distracted with design or implementation details of it’s DOC outside of that which is needed by the SUT.

MockEngine provides the actual implementation of IEngine, and, as you may have guessed, is the actual Mock object implementation of IEngine. MockEngine simply serves to provide a means of verifing that when Car.start is exercised it successfully invokes Engine.run; effectively satisfiying the contract between Car and Engine. MockEngine is implemented as follows:

Figure 4. MockEngine implementation.

MockEngine extends org.mock4as.Mock from which it inherits all of the functionality needed to “Mock” an object, in this case, an IEngine implementation. You’ll notice that MockEngine.run does not implement any “real” functionality, but rather it simply invokes the inherited record method, passing in the method name to record for verification when called. This is the mechanism which allows a MockEngine instance to be verified once run is invoked.

CarTest has been refactored to now provide two distinct tests against Car.start. The first, testStartChangesState(), provides the State Verification test of Car; which tests the expected state of Car after being exercised. The second test, testStartInvokesEngineRun(), provides the actual Behavior Verification test which defines the expectations of the SUT and verification of those expectations on the DOC; that is, Behavior Verification tests are implemented such that they first define expectations, then exercise the SUT, and finally, verify that the expectations have been met. In effect, this verifies that the contract between an SUT and its DOC has been satisfied.

Breaking down the testStartInvokesEngineRun() test, it is quite easy to follow the steps used when writing a Behavior Verification test.

And that’s basically it. While much more can be accomplished with the many Mock Testing frameworks available for Flex, and plenty of information is available on the specifics of the subject, this essay quite necessarily aims to focus on the design benefits of testing with Behavior Verification; that is, the design considerations one must make while doing so.

With Behavior Verification and Mock Objects, design can be guided into existence based on necessity rather than pushed into existence based on implementation.

The example can be downloaded here.

Simple RPC Instrumentation in Flex

On occasion developers may find a need to quickly measure the time it takes for a request to a remote service to return a response back to the client without the need to employ an automated testing tool to perform the instrumentation. This information can prove quite valuable for performing application diagnostics on the client and, when measured in terms of code execution, monitoring at the execution level will always be a bit more precise than that which can be measured by using a Network proxy alone, such as Charles or Fiddler, etc.

Obviously there are numerous solutions which can be implemented to monitor the elapsed time of a service invocation, however it was my goal to provide a unified solution which could easily be implemented into existing client code without significant refactorings being required.

In order to achieve this I first needed to consider what the typical implementation of a service invocation is in order to isolate the
commonality. From there it is only a matter of determining a solution that meets the objective in the most non intrusive manner possible.

To begin let us consider what a “typical” service invocation might look like for the three most common services available in the Flex Framework; HTTPService, RemoteObject and WebService.

Based on the 3 above implementations we can deduce that the common API used when performing a service invocation is AsyncToken. So to provide a unified solution for all three common Services we could either extend AsyncToken or provider an API which wraps AsyncToken. For my needs I chose to implement an API which simply monitors an AsyncToken from which the duration of an invocation can be determined, thus I wrote an RPCDiagnostics API which can be “plugged” into an AsyncToken client implementation.

RPCDiagnostics provides basic performance analysis of a Remote Procedure Call by providing a message which displays information about the operation duration via a standard trace call. In addition, an event listener of type RPCDiagnosticsEvent can be added to facilitate custom diagnostics and Logging.

RPCDiagnostics can easily be implemented as an addition to an existing AsyncToken or in place of an AsyncToken. The following examples demonstrate both implementations.

Implementing RPCDiagnostics onto an existing AsyncToken:

Implementing RPCDiagnostics in place of an AsyncToken:

Implementing a listener to an RPCDiagnostics instance:

The RPCDiagnostics API and dependencies can be downloaded via the Open Source AS3 APIs page or from the below links:

RPCDiagnostics
RPCDiagnosticsEvent
Execution

Design Considerations: Naming Conventions

Intuitive naming conventions are perhaps one of the most important factors in providing a scalable software system. They are essential to ensuring an Object Oriented System can easily be understood, and thus modified by all members of a team regardless of their tenure within the organization or individual experience level.

When classes, interfaces, methods, properties, identifiers, events and the like fail to follow logical, consistent and intuitive naming conventions the resulting software becomes significantly more complex to understand, follow and maintain. As such this makes changes much more challenging than they would have been had better naming been considered originally. Of equal concern is the inevitability that poor naming will lead to redundant code being scattered throughout a project as when the intent of code is not clearly conveyed with as little thought as possible developers tend to re-implement existing functionality when the needed API cannot easily be located or identified.

Code is typically read many, many more times than it is written. With this in mind it is important to understand that the goal of good naming is to be as clear and concise as possible so that a reader of the code can easily determine the codes intent and purpose; just by reading it.

Teams should collectively define a set of standard naming conventions which align well with the typical conventions found in their language of choice. In doing so this will help to avoid arbitrary naming conventions which often result in code that is significantly harder to determine intent, and thus maintain. Of equal importance is the need for various teams from within the same engineering department to standardize on domain specific terms which align with the non-technical terms used by business stakeholders. Together this will help to develop a shared lexicon between business owners and engineers, and allow for simplified analysis of requirements etc.

Ideally, code should follow the PIE Principle (Program, Intently and expressively) – that is, code should clearly convey purpose and intent. In doing so the ability to maintain a software application over time becomes significantly easier and limits the possibility of introducing potential risk to project deliverables.

In short, conventions are very important regardless of a teams size; beit a large collaborative team environment, or a single developer who only deals with his own code. Consistency and conventions are a key aspect to ensuring code quality.

What makes a good design?

One of my core job responsibilities for the past several years has been to conduct technical design and implementation (code) reviews during various phases of the software development life cycle. This is typically a highly collaborative process whereas myself and an individual engineer, or the team as a whole will begin by performing a detailed analysis of business requirements in order to gain an initial understanding of the specific component(s) being developed. Once an understanding of the requirements has been reached a brainstorming session ensues which ultimately leads to various creative, technical solutions. After discussing the pros and cons of each the best solutions quickly begin to reveal themselves, at which point it is simply a process of elimination until the most appropriate solution has surfaced.

The next step is to translate the requirements into the proposed technical solution in the form of a design document. The design is specified on a high level and is only intended to provide an overview of the appropriate technical road map which is to be implemented. This typically consists of higher level UML Sequence and Class diagrams, either in the form of actual diagrams produced in a UML editor, or could simply be a picture captured from UML drawn out during a whiteboarding session. The formality of the documented design is less important, what is important is that the design is captured in some form before it is implemented. Implementation specific details such as exact class and method signatures and so forth are intentionally left out as they are to be considered outside the scope of the design. See Let Design Guide, not Dictate for more on this subject. Once the design is documented it is reviewed and changes are made if needed. This process is repeated until all business and technical requirements have been satisfied, at which point the “all clear” is given to move forward with implementing the design.

But what exactly constitutes a good design? How does one determine a good design from a bad one? In reality it could vary significantly based on a number of factors, however in my experience I have found a design can almost always be judged according to three fundamental criterion: Correctness, Cohesion / Coupling and Scalability. For the most part everything falls into one of these three categories. Below is a brief description of the specific design questions each category sets out to determine.

  • Correctness
    Does the design solve the problems described in the requirements and discussed by the team? This is Correctness in the form of satisfying business requirements. Are the patterns implemented in the design appropriate, or are additional patterns being used just for the sake of using the pattern? This is Correctness in the form of technical requirements. A good design is well focused and only strives to provide a solution which meets the requirements specified by the business owners, client etc; it does not attempt to be overly clever.
  • Cohesion / Coupling
    Has a highly cohesive, loosely coupled design been achieved? Have the classes, interfaces and APIs been logically organized? Does each provide a specific, well-defined set of functionality? Is composition used over inheritance where applicable? Has related functionality been properly abstracted? Does changing this break that, does adding that break this, etc.
  • Scalability
    Does the solution scale well? Is it flexible? A good design strives to facilitate change with confidence, and with as little risk as possible. A good design also achieves transparency at some level in the areas where it is most applicable.

The concepts outlined above are crucial to achieving a good design, however they are often overlooked or misunderstood to some degree. Throughout the years I have began to recognize some commonality in the design mistakes I find in Object Oriented Designs in general, and within Flex projects in particular. Many of which typically can be attributed to violations of basic MVC principles, but most commonly the design mistakes appear to be a negation of Separation of Concerns (SoC).

There are close relationships between Correctness, Cohesion / Coupling and Scalability, each of which plays a very significant role in the resulting design as a whole.

So lets start with Correctness, which is by far the single most important facet of design, for if the design does not provide a solution which satisfies the requirements specified then it has failed – all other aspects of the design are for the most part, details.

It is important to understand that Correctness has a dependency on Flexibility. For example, as architects and developers our understanding of the problem domain is constantly evolving as we gain experience in the domain. Additionally, as requirements may change significantly as a product is being developed, our designs must be able to adapt to these changes as well. Although this poses some challenges it is wrong to suggest that requirements need to be locked down completely before the design phase begins, but rather requirements need only be clearly defined to the extent that the designer is aware of what is required at that point in time and how it fits into the “big picture”. A competent designer understand this well and makes careful considerations before committing to any design decisions. This is where the importance of Flexibility comes into play. In order for a design to be conceptually and technically correct it needs to be flexible enough to support change. This is why good design is so important – to easily facilitate change. As such the flexibility to allow change should be evident throughout the design. A good example might be where the middle-tier has not decided which service layer implementation will be used (e.g. XML:80, WSDL, REST etc.), or the Information Architects have not decided what the constraints of each user role will be. A good design should be flexible enough to allow for changes such as these as well as others with confidence and more importantly, little risk to other parts of the application; after all, you shouldn’t have to tear down the house just to renovate the bathroom – in addition to Correctness and Scalability, this is where Cohesion and Coupling come into play.

High Cohesion is vital to achieving a good design as it ensures related functionality and responsibilities are logically grouped together, encapsulated and abstracted. We have all seen the dreaded, all encompassing class which assumes multiple responsibilities. Classes such as these have low cohesion and are a sign of future challenges if not addressed immediately. On a higher level, if high cohesion had not been achieved it is easy to notice as there will typically only be one class which comprises an entire API, however quite often low cohesion in classes may be a bit more subtle than one might expect and a code review will reveal areas where low cohesion has been implemented.

For example, consider the following Logging facility which is intended to provide a very simple logging implementation:

The above example is such a classic case of low cohesion. I see this kind of thing all the time. The problem here is that the Logger class has low cohesion because it is assuming the responsibility of creating and formatting a time stamp, this functionality is outside of the responsibilities of the Logging API. The creating and formatting of a time stamp is not a concern of the Logger, but rather would be the responsibility of a separate DateFormatting utility whose sole purpose is to provide an API for formatting Date objects. Removing the Date formatting functionality from the Logging API to a class which is responsible for formatting Date objects would facilitate code reuse across many APIs, reduce redundancy and testing as well as allow the Logger class to only define operations which are directly related to Logging. A good design must achieve high cohesion if it is to be successful.

Coupling is essential in determining a good design. A good way to think of coupling is like this: Think back to when you were a kid playing with blocks, you could easily take any number of different blocks and rearrange them to build whatever you like – that’s loose coupling. Now compare that to a crossword puzzle or a jigsaw puzzle, the pieces only fit together in a very specific way – that’s tight coupling. A good design strives to achieve loosely coupled APIs in order to facilitate change as well as reuse. A classic, yet less commonly mentioned example tight coupling is in the packaging of APIs. Often, many times designers will achieve loosely coupled APIs however the APIs themselves are tightly coupled to the application namespace.

Consider the of Logging API example from above, note that the API is defined under the package com.somedomain.someproject.logging. Even if the example were to be refactored to achieve high cohesion it would still be tightly coupled to the project specific namespace. This is a bad design as in the event another product should need to use the Logging API it would first need to be refactored to a common namespace. A better design would be to define the Logging API under the less specific namespace of com.somedomain.logging. This is important as the Logging facility itself should be generic in that it could be used across multiple projects. Something as simple as proper packaging of generic and specific components plays a key role in a good design. A better design for the above example would be as follows, this design achieves both high cohesion and loose coupling:

As with all design, technical design is subjective. Architects and Engineers can spend an infinite amount of time debating the various points of design. In my experience it really comes down to organization and efficiency, that is, organization of responsibilities and concerns, and the efficiency of their implementation both individually and as a whole.

It may sound cliche’ however before you begin a new design, or review an existing one, consider the following quote before doing so – it pretty much sums up what good design is:

“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”
– Antoine de Saint-Exupery