Chapter 2. A comprehensive example

Menu

  • Comprehensive example
  • Doing it wrong
  • Doing it right

Telling you that a sauce béarnaise is “an emulsified sauce made from egg yolk and butter” doesn’t magically instill in you the ability to make one. The best way to learn is to practice; an example can often bridge the gap between theory and practice. Watching a professional cook making a sauce béarnaise is helpful before you try it out yourself.

When I introduced Dependency Injection in the last chapter, I presented a high-level tour to help you understand its purpose and general principles. However, this simple example didn’t do justice to DI. DI is a way to enable loose coupling, and loose coupling is first and foremost an efficient way to deal with complexity.

Most software is complex in the sense that it must address many concerns at the same time. Besides the business concerns (which may be complex in their own right), software must also address concerns related to security, diagnostics, operations, and extensibility. Instead of addressing all of these concerns in one big ball of mud, loose coupling encourages us to address each concern separately. It’s easier to address each concern in isolation—but ultimately we must still compose this complex set of concerns. Let’s take a look at a complex example to better showcase DI.

Figure 2.1. This chapter contains two variations of the same example. First, you’ll see how easy it is to write tightly coupled code. Then, you’ll see how to write the same application in a loosely coupled fashion. Both examples contain the example itself as well as an analysis. If you want to see loosely coupled code right away, you may want to skip the first section.

 

Warning

The whole point of loosely coupled code is to deal with complexity in an efficient manner, and we need complex examples to illustrate complex concepts. You should expect most examples in this book to be complex and involve multiple classes from multiple libraries. Complexity is part of the game.

 

I have devoted this entire chapter to a complex example. I think it’s important to contrast the loosely coupled code with a more “traditional,” tightly coupled example, so in this chapter you’ll find the same feature implemented in both ways. First, I set the stage by showcasing how easy it is to write tightly coupled code. Second, I implement the same functionality using DI. You can skip the tightly coupled example if you want to see some loosely coupled code right away. When you’re done with this chapter, you should begin to understand how you can use DI to compose loosely coupled code.

2.1. Doing it wrong

The idea of building loosely coupled code isn’t particularly controversial, but there’s a huge gap between intent and practice. Before I show you how to use DI to build a loosely coupled application, I want to show you how easily it can go wrong.

A common attempt at loosely coupled code is building a layered application. Anyone can draw a three-layer application diagram, and figure 2.2 proves that I can, too.

Figure 2.2. Standard three-layer application architecture. This is the simplest common variation of the n-layer application architecture, where an application is composed of n layers, each consisting of one or more modules. Some variants of n-layer diagrams will have vertical boxes that span multiple application layers. These are often used to represent CROSS-CUTTING CONCERNS, such as security or logging.

Drawing a three-layer diagram is deceptively simple, but the act of drawing the diagram is akin to stating that you’ll have sauce béarnaise with your steak: it’s a declaration of intent that carries no guarantee with regard to the final result. You may end up with something else, as you shall soon see.

2.1.1. Building a tightly coupled application

There’s more than one way to view and design a flexible and maintainable complex application,[1] but the n-layer application architecture constitutes a well-known, tried-and-true approach. The challenge is to implement it correctly.

1 Currently, the most promising alternative to n-layer applications is an architectural style related to the Command-Query Responsibility Segregation (CQRS) pattern. For more information, see Rinat Abdullin, “CQRS Starting Page,” http://abdullin.com/cqrs

Armed with a three-layer diagram like the one in figure 2.2, you can now start building an application.

Meet Mary Rowan

Mary Rowan is a professional .NET developer working for a local Certified Microsoft Partner that mainly develops web applications. She’s 34-years old and has been working with software for 11 years. This makes her one of the more experienced developers in the company, and she often acts as a mentor for junior developers in addition to performing her regular duties as a senior developer.

In general, Mary is happy about the work that she’s doing, but it frustrates her that milestones are often being missed, forcing her and her colleagues to work long hours and weekends to meet deadlines. She suspects that there must be more efficient ways to build software. In an effort to learn about efficiency, she buys a lot of programming books—but she rarely has time to read them.

Much of her spare time is spent with her husband and two girls. Mary likes to go hiking in the mountains. She’s also an enthusiastic cook, and she definitely knows how to make a real sauce béarnaise.

Mary has been asked to create a new e-commerce application on ASP.NET MVC and the Entity Framework with SQL Server as the data store. To maximize modularity, it must be a three-layer application.

The first feature to be implemented should be a simple list of featured products, pulled from a database table and displayed on a web page; an example is shown in figure 2.3. If the user viewing the list is a preferred customer, the price on all products should be discounted by five percent.

Figure 2.3. Screen shot of the e-commerce web application Mary has been asked to develop. It features a simple list of featured products and their prices (“kr.” is the currency symbol for Danish Kroner).

Let’s look over Mary’s shoulder as she implements the application’s first feature.

Data layer

Because she’ll need to pull data from a database table, Mary has decided to begin by implementing the data layer. The first step is to define the database table itself. Mary uses SQL Server Management Studio to create the table shown in figure 2.4.

Figure 2.4. Mary creates the Product table using SQL Server Management Studio; alternative approaches include writing a T-SQL script or creating the table through Visual Studio, or by some other means.

To implement the Data Access Layer, Mary adds a new library to her solution. From Visual Studio, she uses the Entity Data Model Wizard to generate an entity model from the database she just created. She changes a few names to finalize the model, as shown in figure 2.5.

Figure 2.5. The Product entity generated from the Product database table shown in figure 2.4. Mary has changed the name of the Featured column to IsFeatured, as well as changed a few names in the generated ObjectContext (not shown).

 

Note

Don’t worry if you aren’t familiar with the Microsoft Entity Framework. The details of the data access implementation aren’t that important in this context, so you should be able to follow the example even if you’re more familiar with a different data access technology.

 

The generated ObjectContext and the Product entity are public types contained within the same assembly. Mary knows that she’ll later need to add more features to her application, but the data access component needed to implement the first feature is now completed.

Figure 2.6 shows how far Mary has come in implementing the layered architecture envisioned in figure 2.2.

Figure 2.6. So far Mary has implemented the Data Access Layer of her application. The Domain Logic Layer and User Interface Layer are still left before the feature is complete.

Now that the Data Access Layer has been implemented, the next logical step is the Domain Logic Layer.

Domain Layer

In the absence of any domain logic, the list of Products exposed by the generated ObjectContext could technically have been used directly from the User Interface Layer.

 

Warning

With the exception of pure data-reporting applications, there’s always domain logic. You may not realize it at first, but as you get to know the domain, its embedded and implicit rules and assumptions will gradually emerge. Implementing such logic in either the User Interface or Data Access Layers will lead to pain and suffering. Do yourself a favor and create a Domain Logic Layer from the beginning.

 

The requirements for Mary’s application state that preferred customers should be shown the list prices with a five percent discount. Mary has yet to figure out how to identify a preferred customer, so she asks her coworker Jens for advice:

MARY: I need to implement this business logic so that a preferred customer gets a five percent discount.
JENS: Sounds easy. Just multiply by .95.
MARY: Thanks, but that’s not what I wanted to ask you about. What I wanted to ask you is, how should I identify a preferred customer?
JENS: I see. Is this a web application or a desktop application?
MARY: It’s a web app.
JENS: Okay, then you can define a user profile and have an IsPreferredCustomer property. You can get the profile through the HttpContext.
MARY: Slow down, Jens. This code must be in the Domain Logic Layer. It’s a library. There’s no HttpContext.
JENS: Oh. [Thinks for a while] I still think you should use the Profile feature of ASP.NET to look up the value on the user. You can then pass the value to your domain logic as a Boolean.
MARY: I don’t know...
JENS: That will also ensure that you have good separation of concerns, because your domain logic doesn’t have to deal with security. You know: The SINGLE RESPONSIBILITY PRINCIPLE! It’s the Agile way to do it!
MARY: I guess you’ve got a point.

 

Warning

Jens is basing his advice on his technical knowledge of ASP.NET. As the discussion takes him away from his comfort zone, he steamrolls Mary with a triple combo of buzzwords. Be aware that he doesn’t know what he is talking about: he misuses the concept of separation of concerns, completely misses the point with the SINGLE RESPONSIBILITY PRINCIPLE, and he only mentions Agile because he recently heard someone else talk enthusiastically about it.

 

Armed with Jens’ advice, Mary creates a new C# library project and adds a class called ProductService, shown in the following listing. To make the ProductService class compile, she must add a reference to her Data Access library, because the CommerceObjectContext class is defined there.

Listing 2.1. Mary’s ProductService class
public partial class ProductService
{
    private readonly CommerceObjectContext objectContext;
    public ProductService()
    {
        this.objectContext = new CommerceObjectContext();
    }

    public IEnumerable<Product> GetFeaturedProducts(
        bool isCustomerPreferred)
    {
        var discount = isCustomerPreferred ? .95m : 1;
        var products = (from p in this.objectContext
                            .Products
                        where p.IsFeatured
                        select p).AsEnumerable();
        return from p in products
               select new Product
               {
                   ProductId = p.ProductId,
                   Name = p.Name,
                   Description = p.Description,
                   IsFeatured = p.IsFeatured,
                   UnitPrice = p.UnitPrice * discount
               };
    }
}

Mary is happy that she has encapsulated data access technology (LINQ to Entities), configuration, and domain logic in the ProductService class. She has delegated the knowledge of the user to the caller by passing in the isCustomerPreferred parameter, and she uses this value to calculate the discount for all the products.

Further refinement could include replacing the hard-coded discount value (.95) with a configurable number, but, for now, this implementation will suffice. Mary is almost done—the only thing still left is the User Interface. Mary decides that can wait until the next day.

Figure 2.7 shows how far Mary has come with implementing the architecture envisioned in figure 2.2.

Figure 2.7. At this point, Mary has implemented the Data Access Layer and the Domain Logic Layer. Compared to figure 2.6, the Domain Logic Layer has been added. The User Interface Layer still remains to be implemented.

With the Data Access and Domain Logic Layers implemented, the only remaining layer to implement is the User Interface Layer.

User Interface Layer

The next day, Mary resumes her work with the e-commerce application, adding a new ASP.NET MVC application to her solution.

 

Note

Don’t worry if you aren’t familiar with the ASP.NET MVC framework. The intricate details of how the MVC framework operates aren’t the focus of this discussion. The important part is how DEPENDENCIES are consumed, and that’s a relatively platform-neutral subject.

 

 

ASP.NET MVC crash course

ASP.NET MVC takes its name from the Model View Controller[2] design pattern. In this context, the most important thing to understand is that when a web request arrives, a Controller handles the request, potentially using a (Domain) Model to deal with it and form a response that’s finally rendered by a View.

2 Martin Fowler et al., Patterns of Enterprise Application Architecture (New York: Addison-Wesley, 2003), 330.

A Controller is normally a class that derives from the abstract Controller class. It has one or more action methods that handle requests; for example, a HomeController class has an Index method that handles the request for the default page.

When an action method returns, it passes on the resulting Model to the View through a ViewResult instance.

 

The next listing shows how she implements an Index method on her HomeController class to extract the featured products from the database and pass them to the View. To make this code compile, she must add references to both the Data Access library and the Domain library because the ProductService class is defined in the Domain library, but the Product class is defined in the Data Access library.

Listing 2.2. Index method on the default controller class
public ViewResult Index()
{
    bool isPreferredCustomer =
        this.User.IsInRole("PreferredCustomer");

    var service = new ProductService();
    var products =
        service.GetFeaturedProducts(isPreferredCustomer);
    this.ViewData["Products"] = products

    return this.View();
}

As part of the ASP.NET MVC lifecycle, the User property on the HomeController class is automatically populated with the correct user object, so Mary uses it to determine if the current user is a preferred customer. Armed with this information, she can invoke the Domain Logic to get the list of featured products. In a moment, I’ll return to this, because it contains a trap, but for now, I’ll let Mary discover it for herself.

The list of products must be rendered by the Index View. The following listing shows the markup for the View.

Listing 2.3. Index View markup

ASP.NET MVC enables you to write standard HTML with bits of imperative code embedded to access objects created and assigned by the Controller that created the View. In this case, the HomeController’s Index method assigned the list of featured products to a key called Products that Mary uses in the View to render the list of products.

Figure 2.8 shows how Mary has now implemented the architecture envisioned in figure 2.2.

Figure 2.8. Mary has now implemented all three layers in the application. This figure is identical to figure 2.2, but repeated here to illustrate the current state of Mary’s application.

With all three layers in place the applications should theoretically work, but only a test can verify whether that’s the case.

2.1.2. Smoke test

Mary has now implemented all three layers, so it’s time to see if the application works. She presses F5 and soon receives this message:

The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid.

Because Mary used the default constructor of CommerceObjectContext (shown in listing 2.1), it implicitly expects that a connection string named CommerceObjectContext is present in the web.config file. As I alluded to when I discussed listing 2.2, this implicitness contains a trap. During the night, Mary forgot the implementation details of her Domain Layer. The code compiles, but the site doesn’t work.

In this case, fixing the error is straightforward. Mary inserts the correct connection string in the web.config file. When she runs the application, the web page shown in figure 2.3 appears.

The Featured Products feature is now done, and Mary feels confident and ready to implement the next feature in the application. After all, she has followed established best practices and created a three-layer application.

2.1.3. Evaluation

Did Mary succeed in developing a proper, layered application? No, she did not—although she certainly had the best of intentions. She created three Visual Studio projects that correspond to the three layers in the planned architecture, as shown in figure 2.9. To the casual observer, this looks like the coveted layered architecture, but, as you’ll see, the code is tightly coupled.

Figure 2.9. Mary’s e-commerce web application has a Visual Studio project for each layer in the planned architecture—but is it a three-layer application?

Visual Studio makes it easy and natural to work with solutions and projects in this way. If we need functionality from a different library, we can easily add a reference to it and write code that creates new instances of the types defined in these other libraries. Every time we add a reference, we take on a DEPENDENCY.

Dependency graph

When working with solutions in Visual Studio, it’s easy to lose track of the important DEPENDENCIES, because Visual Studio displays them together with all the other project references that may point to assemblies in the .NET Base Class Library (BCL).

To understand how the modules in Mary’s application relate to each other, we can draw a graph of the dependencies (see figure 2.10).

Figure 2.10. Dependency graph for Mary’s application showing how the modules depend on each other. The arrows point towards a module’s DEPENDENCY.

The most remarkable insight to be gained from figure 2.10 is that the User Interface library is dependent on both Domain and Data Access libraries. It seems as though the User Interface may bypass the Domain Layer in certain cases. This bears further investigation.

Evaluating composability

A major goal of building a three-layer application is to separate concerns. We’d like to separate our Domain Model from the Data Access Layer and the User Interface Layer so that none of these concerns pollute the Domain Model. In large applications, it’s essential for it to be possible to work with one area of the application in isolation.

To evaluate Mary’s implementation, we can ask a simple question:

 

Test

Is it possible to use each module in isolation?

 

In theory, we should be able to compose modules in any way we like. We may need to write new modules to bind existing modules together in new and unanticipated ways, but, ideally, we should be able to do so without having to modify the existing modules.

 

Note

The following analysis discusses whether modules can be replaced, but be aware that this is a technique we use to evaluate composability. Even if we never want to swap modules, this sort of analysis uncovers potential issues regarding coupling. If we find that the code is tightly coupled, all the benefits of loose coupling are lost.

 

Can we use the modules in Mary’s application in new and exciting ways? Let’s look at some likely scenarios.

New user interface

If Mary’s application becomes a success, the project’s stakeholders would like her to develop a rich client version in Windows Presentation Foundation (WPF). Is this possible to do while reusing the Domain Layer and the Data Access Layer?

When we examine the dependency graph in figure 2.10, we can quickly ascertain that no modules are depending on the Web User Interface, so it’s possible to remove it and replace it with a WPF User Interface.

Creating a rich client based on WPF is a new application that shares most of its implementation with the original web application. Figure 2.11 illustrates how a WPF application would need to take the same dependencies as the web application. The original web application can remain unchanged.

Figure 2.11. Replacing the Web User Interface with a WPF User Interface is possible because no module depends on the Web User Interface. The original Web User Interface remains in the figure in grayscale to illustrate the point that adding a new user interface doesn’t preclude the original.

Replacing the User Interface Layer is certainly possible with Mary’s implementation, so let’s examine another interesting decomposition.

New Data Access Layer

Imagine that market analysts figure out that, to optimize profits, Mary’s application should be available as a cloud application hosted on Windows Azure. In Windows Azure, data can be stored in the highly scalable Azure Table Storage Service. This storage mechanism is based on flexible data containers that contain unconstrained data. The service enforces no particular database schema, and there’s no referential integrity.

The protocol used to communicate with the Table Storage Service is HTTP, and the most obvious data access technology on .NET is based on ADO.NET Data Services.

This type of database is sometimes known as a key-value database, and it’s a different beast than a relational database accessed through the Entity Framework.

Figure 2.12. Attempting to remove the relational Data Access library leaves nothing left, because all other modules depend on it. There’s no place where we can instruct the Domain library to use the new Azure Table Data Access library instead of the original.

To enable the e-commerce application as a cloud application, the Data Access library must be replaced with a module that uses the Table Storage Service. Is this possible?

From the dependency graph in figure 2.10, we already know that both User Interface and Domain libraries depend on the Entity Framework-based Data Access library. If we try to remove the Data Access library, the solution will no longer compile, because a required DEPENDENCY is missing.

In a big application with dozens of modules, we could also try to remove those modules that don’t compile to see what would be left. In the case of Mary’s application, it’s evident that we’d have to remove all modules, leaving nothing behind.

Although it would be possible to develop an Azure Table Data Access library that mimics the API exposed by the original Data Access library, there’s no way we could inject it into the application.

The application isn’t nearly as composable as the project stakeholders would have liked. Enabling the profit-maximizing cloud abilities requires a major rewrite of the application, because none of the existing modules can be reused.

Other combinations

We could analyze the application for other combinations of modules, but it would be a moot point because we already know that it fails to support an important scenario.

Besides, not all combinations make sense. We could ask whether it would be possible to replace the Domain Model with a different implementation. In most cases, this would be an odd question to ask, because the Domain Model encapsulates the heart of the application. Without the Domain Model, most applications have no raison d’être (reason for being).

2.1.4. Analysis

Why did Mary’s implementation fail to achieve the desired degree of composability? Is it because the User Interface has a direct dependency on the Data Access library? Let’s examine this possibility in greater detail.

Dependency graph analysis

Why is the User Interface dependent on the Data Access library? The culprit is this Domain Model method signature:

The GetFeaturedProducts method returns a sequence of products, but the Product class is defined in the Data Access library. Any client consuming the GetFeaturedProducts method must reference the Data Access library to be able to compile.

It’s possible to change the signature of the method to return a sequence of a type defined within the Domain Model. It would also be more correct, but it doesn’t solve the problem.

Let’s assume that we break the dependency between the User Interface and Data Access libraries. The modified dependency graph would now look like figure 2.13.

Figure 2.13. Dependency graph of the hypothetical situation where the dependency of the User Interface on the Data Access library has been severed.

Would such a change enable Mary to replace the relational Data Access library with one that encapsulates access to the Azure Table service? Unfortunately, no, because the Domain library still depends on the Data Access library. The User Interface, in turn, still depends on the Domain Model, so if we try to remove the original Data Access library, there would be nothing left of the application.

The root cause of the problem lies somewhere else.

Data Access interface analysis

The Domain Model depends on the Data Access library because the entire data model is defined there. The Product class was generated when Mary ran the LINQ to Entities wizard. Using the Entity Framework to implement a Data Access Layer may be a reasonable decision.

However, consuming it directly in the Domain Model isn’t.

The offending code can be found spread out in the ProductService class. The constructor creates a new instance of the CommerceObjectContext class and assigns it to a private member variable:

this.objectContext = new CommerceObjectContext();

This tightly couples the ProductService class to the Data Access library. There’s no reasonable way we can intercept this piece of code and replace it with something else. The reference to the Data Access library is hard-coded into the ProductService class.

The implementation of the GetFeaturedProducts method uses the CommerceObjectContext to pull Product objects from the database:

var products = (from p in this.objectContext
                    .Products
                where p.IsFeatured
                select p).AsEnumerable();

This only reinforces the hard-coded dependency, but, at this point, the damage is already done. What we need is a better way to compose modules without such tight coupling.

Miscellaneous other issues

Before I show you the better alternative, I’d like to point out a few other issues with Mary’s code that ought to be addressed.

  • Most of the Domain Model seems to be implemented in the Data Access library. Whereas it’s a technical problem that the Domain Model library references the Data Access library, it’s a conceptual problem that the Data Access library defines such a class as the Product class. A public Product class belongs in the Domain Model.
  • Under the influence of Jens, Mary decided to implement the code that determines whether or not a user is a preferred customer in the User Interface. However, how a customer is identified as a preferred customer is a piece of Business Logic, so it ought to be implemented in the Domain Model. Jens’ argument about separations of concern and the SINGLE RESPONSIBILITY PRINCIPLE is no excuse for putting code in the wrong place. Following the SINGLE RESPONSIBILITY PRINCIPLE within a single library is entirely possible—that’s the expected approach.
  • The ProductService class relies on XML configuration. As you saw when we followed Mary’s efforts, she forgot that she had to put a particular piece of configuration code in her web.config file. Although the ability to configure a compiled application is important, only the finished application should rely on configuration files. It’s much more flexible for reusable libraries to be imperatively configurable by their callers. In the end, the ultimate caller is the application itself. At that point, all relevant configuration data can be read from a .config file and fed to the underlying libraries, as needed.
  • The View (as shown in listing 2.3) seems to contain too much functionality. It performs casts and specific string formatting. Such functionality should be moved to the underlying model.

In the next section, I’ll show you a more composable way of building an application with the same features as the one Mary built. I’ll also address these minor issues at the same time.

2.2. Doing it right

Dependency Injection (DI) can be used to solve the issues that we discovered. Because DI is a radical departure from the way Mary created her application, I’m not going to modify it. Rather, I’m going to re-create it from scratch.

You shouldn’t infer from this decision that it’s impossible to refactor an existing application towards DI; it can be done, but it’s hard. In my experience, it takes a lot of refactorings to get there.[3]

3 There’s a whole book about this subject. See Michael Feathers, Working Effectively with Legacy Code (New York: Prentice Hall, 2004).

 

Note

As I walk you through this example, don’t worry if you get lost along the way. DI is complex, and there are many elements in play. I chose this example because it resembles a realistic scenario, but the disadvantage is that it’s more complex than a toy example. Later in the book, I’m going to delve deeper into the concepts and techniques that are being introduced here. After you’ve read more, you can always come back and reread this section.

 

Many people refer to DI as INVERSION OF CONTROL (IoC). These two terms are sometimes used interchangeably, but DI is a subset of IoC. Throughout the book, I’ll consistently use the most specific term: DI. If I mean IoC, I’ll refer to it specifically.

 

Dependency Injection or Inversion of Control?

The term Inversion of Control (IoC) originally meant any sort of programming style where an overall framework or runtime controlled the program flow.[4] According to that definition, most software developed on the .NET Framework uses IoC.

4 Martin Fowler, “InversionOfControl,” 2005, http://martinfowler.com/bliki/InversionOfControl.html

When you write an ASP.NET application, you hook into the ASP.NET page life cycle, but you aren’t in control—ASP.NET is.

When you write a WCF service, you implement interfaces decorated with attributes. You may be writing the service code, but ultimately, you aren’t in control—WCF is.

These days, we’re so used to working with frameworks that we don’t consider this to be special, but it’s a different model from being in full control of your code. This can still happen for a .NET application—most notably for command-line executables. As soon as Main is invoked, your code is in full control. It controls program flow, lifetime, everything. No special events are being raised and no overridden members are being invoked.

Before DI had a name, people started to refer to frameworks that manage DEPENDENCIES as Inversion of Control Containers, and soon, the meaning of IoC gradually drifted towards that particular meaning: Inversion of Control over DEPENDENCIES. Always the taxonomist, Martin Fowler introduced the term Dependency Injection to specifically refer to IoC in the context of dependency management.[5]Dependency Injection has since been widely accepted as the most correct terminology.

5 Martin Fowler, “Inversion of Control Containers and the Dependency Injection pattern,” 2004, http://martinfowler.com/articles/injection.html

In short, IoC is a much broader term that includes, but isn’t limited to, DI.

 

In the context of managing dependencies, INVERSION OF CONTROL accurately describes what we’re trying to accomplish. In Mary’s application, the code directly controls its dependencies: when the ProductService needs a new instance of the CommerceObjectContext class, it simply creates an instance using the new keyword. When the HomeController needs a new instance of the ProductService class, it, too, news up an instance. The application is in total control. That may sound powerful, but it’s actually limiting. I call this the CONTROL FREAK anti-pattern. INVERSION OF CONTROL instructs us to let go of that control and let something else manage the dependencies.

2.2.1. Rebuilding the commerce application

When I write software, I prefer to start in the most significant place. This is often the user interface. From there, I work my way in, adding more functionality until the feature is done and I can move on to the next. This outside-in technique helps me to focus on the requested functionality without over-engineering the solution.

 

Note

The outside-in technique is closely related to the YAGNI principle (“You Aren’t Gonna Need It”). This principle emphasizes that only required features should be implemented, and that the implementation should be as simple as possible.

 

Because I always practice Test-Driven Development (TDD), I start by writing unit tests as soon as my outside-in approach prompts me to create a new class. Although I wrote a lot of unit tests to create this example, TDD isn’t required to implement and use DI, so I’m not going to show these tests in the book. If you’re interested, they’re available in the source code that accompanies the book.

User interface

The specification for the list of featured products is to write an application that extracts the featured products from the database and displays them in a list, as shown in figure 2.3. Because I know that the project’s stakeholders will mainly be interested in the visual result, the User Interface sounds like a good place to start.

The first thing I do after opening Visual Studio is to add a new ASP.NET MVC application to my solution. Because the list of featured products needs to go on the front page, I start by modifying the Index.aspx to include the markup shown in the following listing.

Listing 2.4. Index View markup
<h2>Featured Products</h2>
<div>
<% foreach (var product in this.Model.Products)
   { %>
   <div><%= this.Html.Encode(product.SummaryText) %></div>
<% } %>
</div>

Notice how much cleaner listing 2.4 is compared to listing 2.3. The first improvement is that it’s no longer necessary to cast a dictionary item to a sequence of products before iteration is possible. I accomplished this easily by letting the Index.aspx page inherit from System.Web.Mvc.ViewPage<FeaturedProductsViewModel> instead of System.Web.Mvc.ViewPage. This means that the Model property of the page is of the FeaturedProductsViewModel type.

The entire product display string is pulled directly from the SummaryText property of the product.

Both improvements are related to the introduction of View-specific Models that encapsulate the behavior of the View. These Models are Plain Old CLR[6] Objects (POCOs). Figure 2.14 provides an outline of their structure.

6 Common Language Runtime

Figure 2.14. The FeaturedProductsViewModel contains a list of ProductViewModels. Both are POCOs, which makes them eminently susceptible to unit testing. The SummaryText property is derived from the Name and UnitPrice properties to encapsulate rendering logic.

The HomeController must return a View with an instance of FeaturedProductsViewModel for the code in listing 2.4 to work. As a first step, this can be implemented inside the HomeController like this:

public ViewResult Index()
{
    var vm = new FeaturedProductsViewModel();
    return View(vm);
}

This will enable the web application to execute without error, but the list of featured products will always be empty. Providing the list of featured products is a task for the Domain Model.

Figure 2.15 shows the current state of implementing the architecture envisioned in figure 2.2.

Figure 2.15. At this stage, only the User Interface Layer has been implemented, and the Domain Logic and Data Access Layers still remain. Contrast this figure with figure 2.6, which shows Mary’s progress at a comparable stage. One advantage of starting with the user interface is that we already have software we can run and test. Only at a much later stage, shown in figure 2.8, does Mary arrive at a point where she can run the application.

Although a user interface exists, it doesn’t do much of interest. The list of featured products is always empty, so I need to implement some Domain Logic that can supply a proper list of products.

Domain Model

The Domain Model is a plain vanilla C# library that I add to the solution. This library will contain POCOs and abstract types. The POCOs will model the Domain while the abstract types provide abstractions that will serve as my main external entry point into the Domain Model.

The principle of programming to interfaces instead of concrete classes is a cornerstone of DI. It’s this principle that allows us to replace one concrete implementation with another.

 

Interfaces or abstract classes?

Many guides to object-oriented design focus on interfaces as the main abstraction mechanism, whereas the .NET Framework Design Guidelines endorse abstract classes over interfaces.[7] Should you use interfaces or abstract classes?

7 Krzysztof Cwalina and Brad Abrams, Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (New York, Addison-Wesley, 2006), 77-83.

With relation to DI, the reassuring answer is that it doesn’t matter. The important part is that you program against some sort of abstraction.

Choosing between interfaces and abstract classes is important in other contexts, but not here. You’ll notice that I use the words interchangeably; I often use the term ABSTRACTION to encompass both interfaces and abstract classes.

 

I’m still following the outside-in approach, so I’ll be adding code to the User Interface Layer for a while yet. Some of the code I’ll add will now use types from the Domain Model. This means that I’ll add a reference to the Domain Model from the User Interface, like Mary did. That will turn out okay, but I’ll postpone doing a dependency graph analysis until section 2.2.2, so that I can provide you with the full picture.

A common abstraction over data access is provided by the Repository pattern,[8] so I’ll define a ProductRepository abstract class in the Domain Model library:

8 Fowler, Patterns of Enterprise Application Architecture, 322-327.

public abstract class ProductRepository
{
    public abstract IEnumerable<Product> GetFeaturedProducts();
}

A full-blown Repository would have more methods to find and modify products, but, following the outside-in principle, I only define the classes and members I need for the task at hand. It’s easier to add functionality to code than it is to remove anything.

The Product class is also implemented with the bare minimum of members, as illustrated in figure 2.16.

Figure 2.16. The Product class only contains the Name and UnitPrice properties, because these are the only properties needed to implement the desired application feature. The ApplyDiscountFor applies the discount (if any) for a user and returns an instance of the DiscountedProduct class. The abstract GetFeaturedProducts returns a sequence of Products.

The Index method on HomeController should use a ProductService instance to retrieve the list of featured products, apply any discounts, convert the Product instances to ProductViewModel instances, and add them to the FeaturedProductsViewModel. Because the ProductService class takes an instance of ProductRepository in its constructor, the tricky part is to provide it with a proper instance. Recall from the analysis of Mary’s implementation that newing up dependencies is a cardinal sin. As soon as I do that, I’m tightly coupled with the type I just used.

I’m going to relinquish control of the ProductRepository dependency. As shown in the next listing, I’d rather be relying on something else to provide me with an instance through the HomeController’s constructor. This pattern is called CONSTRUCTOR INJECTION—how the instance is created, and by whom, is of no concern to the HomeController.

Listing 2.5. HomeController with CONSTRUCTOR INJECTION

The HomeController constructor specifies that anyone wishing to use the class must provide an instance of ProductRepository (which, as you may remember, is an abstract class). A Guard Clause[9] guarantees this precondition by throwing an exception if the supplied instance is null. The injected dependency can be saved for later and safely used by other members of the HomeController class.

9 Martin Fowler et al., Refactoring: Improving the Design of Existing Code (New York: Addison-Wesley, 1999), 250.

The first time I heard about CONSTRUCTOR INJECTION, I had a hard time understanding the real benefit. Doesn’t it push the burden of controlling the DEPENDENCY onto some other class?

Yes, it does—and that’s the whole point. In an n-layer application, we can push that burden all the way to the top of the application, into a COMPOSITION ROOT. This is a centralized place where the different modules of an application can be composed. This can be done manually or delegated to a DI CONTAINER.

The HomeController delegates most of its work to the ProductService class, shown in the following listing. The ProductService class corresponds to Mary’s class of the same name, but is now a pure Domain Model class.

Listing 2.6. ProductService class

The GetFeaturedProducts method now takes an instance of IPrincipal that represents the current user. This is another departure from Mary’s implementation in listing 2.1, which only took a Boolean value, indicating whether the user is a preferred customer. However, because deciding whether a user is a preferred customer is a piece of Domain Logic, it’s more correct to explicitly model the current user as a DEPENDENCY. We must always adhere to the principle of programming to an interface, but in this case I don’t need to invent one (as I did with ProductRepository) because the .NET Base Class Library (BCL) already includes the IPrincipal interface, which represents a standard way of modeling application users.

Passing a DEPENDENCY as a parameter in a method is known as METHOD INJECTION. Once again, control is delegated to the caller, similar to CONSTRUCTOR INJECTION. Although the details vary, the main technique remains the same.

At this stage, the application doesn’t work at all. There are two problems left:

  • There are no concrete implementations of ProductRepository. This is easily solved. In the next section, I’ll implement a concrete ProductRepository that reads the featured products from the database.
  • By default, ASP.NET MVC expects Controllers to have default constructors. Because I introduced a parameter to HomeController’s constructor, the MVC framework doesn’t know how to create an instance of HomeController. This issue can be solved by developing a custom IControllerFactory. How this is done is outside the scope of this chapter, but it’s a subject that will be discussed in chapter 7. Suffice it to say that this custom factory will create an instance of the concrete ProductRepository and supply it to HomeController’s constructor.

In the Domain Model, I work only with types defined within the Domain Model (and the .NET Base Class Library). The concepts of the Domain Model are implemented as POCOs. At this stage, there’s only a single concept represented, namely, a Product. The Domain Model must be able to communicate with the outside world (such as databases). This need is modeled as abstract classes (such as Repositories) that we must replace with concrete implementations before the Domain Model becomes useful.

Figure 2.17 shows the current state of implementing the architecture envisioned in figure 2.2.

Figure 2.17. The User Interface and Domain Logic Layers are now both in place, while the Data Access Layer remains to be implemented. Contrast this figure with figure 2.7, which shows Mary’s progress at a similar stage.

The application’s Domain Model isn’t yet particularly object-oriented;[10] there’s only the single abstract ProductRepository that I need to implement to close the loop.

10 We call this an Anemic model. Martin Fowler, “AnemicDomainModel,” 2003, http://www.martinfowler.com/bliki/AnemicDomainModel.html

Data access

Like Mary, I’d like to implement my Data Access library using LINQ to Entities, so I follow the same steps as she did in section 2.1.1 to create the Entity Model. The main difference is that the Entity Model and the CommerceObjectContext are now only implementation details; but, with them, I can create an implementation of ProductRepository, as shown in the following listing.

Listing 2.7. Implementing ProductRepository using LINQ to Entities

In Mary’s application, the generated Product entity was used as a Domain object, although it was defined in the database. This is no longer the case, because I already defined the Product class in the Domain Model. When I generated the Entity Model, the wizard created another Product class for me and I need to convert between the two . Figure 2.18 illustrates how they’re defined in two different modules. The Product entity class is merely an implementation detail, and I could easily have made it internal to express that more explicitly.

Figure 2.18. The Domain Model and Data Access libraries both define a class named Product. The Domain Product is the important class that encapsulates the Domain concept of a Product. The Data Access Product class is only an artifact of the Entity Framework wizard. It can easily be renamed or made internal.

 

Note

You could argue that it’s a specific shortcoming of the Entity Framework that it doesn’t support persistence-ignorant entities[11] (at least not in the .NET 3.5 SP1 version). However, this is the sort of constraint you must deal with in real software projects.

11 For a good introduction to persistence ignorance, see Jeremy Miller, “Patterns in Practice: The Unit Of Work Pattern And Persistence Ignorance,” (MSDN Magazine, June 2009). Also available online at http://msdn.microsoft.com/en-us/magazine/dd882510.aspx

 

The Entity Product defines a conversion to the Domain Product type. This conversion is a trivial mapping of property values. Although not particularly related to DI, I include it here for good measure:

Domain.Product p = new Domain.Product();
p.Name = this.Name;
p.UnitPrice = this.UnitPrice;
return p;

With SqlProductRepository implemented, I can now set up ASP.NET MVC to inject an instance of it into instances of HomeController. Because I discuss this in greater detail in chapter 7, I don’t show that here.

Figure 2.19 shows the current state of the application architecture as envisioned in figure 2.2.

Figure 2.19. All three layers in the application are now implemented as envisioned in figure 2.2. This figure is identical, but is repeated here to illustrate the current state of the application. The figure is also identical to figure 2.8, which shows Mary’s completed application.

Now that everything is correctly wired together, I can browse to the application’s homepage and get the same page as shown in figure 2.3.

2.2.2. Analyzing the loosely coupled implementation

The previous section contained lots of details, so it’s hardly surprising if you lost sight of the big picture along the way. In this section, I’ll try to explain what happened in broader terms.

Interaction

The classes in each layer interact with each other in either direct or abstract form. They do so across module boundaries, so it can be difficult to follow how they interact. Figure 2.20 illustrates how dependencies are being connected.

Figure 2.20. Interaction between elements involved in Dependency Injection in the Commerce application. Notice how the SqlProductRepository instance is injected into the HomeController, and then again later, through the HomeController into a ProductService that ultimately uses it.

When the application starts, the code in Global.asax creates a new custom Controller factory. The application keeps a reference to the Controller factory, so when a page request comes in, the application invokes CreateController on the factory. The factory looks up the connection string from web.config and supplies it to a new instance of SqlProductRepository. It injects the SqlProductRepository instance into a new instance of HomeController and returns that instance.

The application then invokes the Index method on the HomeController instance, causing it to create a new instance of ProductService, passing the SqlProductRepository instance to it in its constructor. The ProductService invokes the GetFeaturedProducts method on the SqlProductRepository instance.

Finally, the ViewResult with the populated FeaturedProductsViewModel is returned, and the ASP.NET MVC framework finds and renders the correct page.

Dependency graph

In section 2.1.3, we saw how a dependency graph can help us analyze and understand the degree of flexibility provided by the architectural implementation. Has DI changed the dependency graph for the application?

Figure 2.21 shows that the dependency graph has indeed changed. The Domain Model no longer has any dependencies and can act as a stand-alone module. On the other hand, the Data Access library now has a dependency; in Mary’s application, it had none.

Figure 2.21. Dependency graph showing the example commerce application when Dependency Injection is applied. The most notable difference is that the Domain library no longer has any dependencies. The grey boxes within the black boxes show sample classes in each library to give you an idea which classes go where. There are more classes in each library than the ones shown.

This should raise our hopes that we can answer the original questions about composability more favorably this time:

  • Can we replace the web-based user interface with a WPF-based user interface? That was possible before, and is still possible with the new design. Neither the Domain Model nor the Data Access libraries depend on the web-based user interface, so we can easily put something else in its place.
  • Can we replace the relational Data Access library with one that works with the Azure Table Service? In chapter 3 I’ll describe how the application locates and instantiates the correct ProductRepository, so for now take the following at face value: the Data Access library is being loaded by late binding, and the type name is defined as an application setting in web.config. It’s possible to throw the current Data Access library away and inject a new one, as long as it also provides an implementation of ProductRepository.

It’s no longer possible to use the current Data Access library in isolation, as it now depends on the Domain Model. In many types of applications, that’s not an issue, but if the stakeholders want that feature, I can solve the problem by adding another layer of indirection: by extracting an interface from Product (say, IProduct) and changing ProductRepository to work with IProduct instead of Product. These abstractions can then be moved to a separate library that’s shared by both the Data Access library and the Domain Model. It would require more work, because I’d need to write code to map between Product and IProduct, but it’s certainly possible.

With the DI-based design, the original web application can be gradually transformed to a Software + Services application with a rich WPF interface and a cloud-based storage engine. The only thing remaining from the initial effort is the Domain Model, but that’s only appropriate because it encapsulates all the important business rules and, as such, we should expect that to be the most essential module.

When we develop applications, we can’t possibly foresee every future direction we may need to take the product, but that’s no problem as long as we can keep our options open. DI helps us build loosely coupled applications so that we can reuse or replace different modules as needed.

2.3. Expanding the sample application

To support the rest of the book and fully demonstrate different aspects of DI, I’ll need to expand the sample commerce application. Until now, I have kept the application as simple and small as possible to gently introduce some core concepts and principles. Because one of the main purposes of DI is to manage complexity, we need a complex application to fully appreciate its power.

I’ll expand the application along two axes: an architectural refactoring and an added feature.

2.3.1. Architecture

So far, the sample application has been a three-layer application, but now I want to slide a Presentation Model layer in between the UI and the Domain Model, as shown in figure 2.22.

Figure 2.22. A Presentation Model layer is inserted into the sample application to separate the presentation logic from the application root.

I move all the Controllers and ViewModels from the User Interface Layer to the Presentation Model layer, leaving only the Views (the .aspx and .ascx files) and the COMPOSITION ROOT in the User Interface Layer.

The main reason for this move is to separate the COMPOSITION ROOT from the presentation logic; this way, I can show you different variations of configuration styles while keeping invariant as much of the application as possible.

 

Humble Object

It isn’t only for educational purposes that I split up the application into a User Interface Layer and a Presentation Model layer; I routinely do this for all applications I write if they have a user interface at all.

This split provides clear separation of concerns between presentation logic (how user interfaces behave) and rendering (how user interfaces look). It puts all logic into a layer where it can be unit tested, and puts all markup in a layer where a graphic designer can work without fear of breaking things too easily.

The goal is to have as little imperative code as possible in the User Interface Layer, because I’m not going to write any unit tests for this layer.

An application root that contains only the bare minimum of code to bootstrap itself, after which it delegates all other work to TESTABLE modules, is called a Humble Object.[12] In this case, it contains only Views and bootstrap code: the COMPOSITION ROOT.

12 Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code (New York: Addison-Wesley, 2007), 695-708.

 

Apart from this architectural change, I also want to add a richer feature than what we’ve been looking at so far.

2.3.2. Basket feature

The list of featured products only presents us with a limited level of complexity: there’s only a single Repository involved in a read-only scenario.

The logical next step is to introduce a shopping basket feature. Figure 2.23 shows a screenshot of the shopping basket in use.

Figure 2.23. The spectacularly feature-poor shopping basket in the refactored commerce sample application.

To support a shopping basket for each user, I need a Basket, a BasketRepository, and a host of supporting classes. If you’re like me, you want to see the Basket class first: figure 2.24 shows the basket and its list of items.

Figure 2.24. Basket and its Contents, which is a list of Extent<EvaluatedProduct>. An Extent represents a quantity of a given product.

From a DI perspective, the Basket and Extent classes aren’t particularly interesting: they’re both Plain Old CLR Object (POCO) classes with no DEPENDENCIES. Of much more interest is the BasketService and supporting classes, shown in figure 2.25.

Figure 2.25. BasketService and supporting classes. A BasketService can retrieve and evaluate a Basket for a given user. It uses a BasketRepository to retrieve the Basket and a BasketDiscountPolicy to apply discounts (if any).

A BasketService can be used to retrieve a user’s Basket and apply discounts. It uses the abstract BasketRepository to get the contents of the Basket, and the abstract BasketDiscountPolicy to apply discounts. Both of these ABSTRACTIONS are injected into the BasketService via CONSTRUCTOR INJECTION:

public BasketService(BasketRepository repository,
    BasketDiscountPolicy discountPolicy)

A BasketDiscountPolicy can be a simple implementation with a hard-coded policy, such as giving preferred customers a five percent discount, as we saw earlier in this chapter. This policy is implemented by the DefaultProductDiscountPolicy, while a more complex, data-driven implementation is provided by RepositoryBasketDiscountPolicy, that itself uses the abstract DiscountRepository to get a list of discounted products. This ABSTRACTION is once again injected into the RepositoryBasketDiscountPolicy via CONSTRUCTOR INJECTION:

public RepositoryBasketDiscountPolicy(DiscountRepository repository)

To manage all this, I can use the BasketService to orchestrate the operations on the Basket: adding items, as well as displaying and emptying the Basket. To do this, it needs both a BasketRepository and a BasketDiscountPolicy that (you guessed it) is supplied to it via its constructor:

public BasketService(BasketRepository repository,
    BasketDiscountPolicy discountPolicy)

To further complicate matters, I need an ASP.NET MVC controller called BasketController that wraps around the IBasketService interface that I again inject into it via its constructor:

public BasketController(IBasketService basketService)

As figure 2.25 shows, the BasketService class implements IBasketService, so that’s the implementation we use.

The BasketController is ultimately created by a custom IControllerFactory, so it will need these ABSTRACTIONS as well.

If you lost track along the way, figure 2.26 shows a diagram that illustrates how the DEPENDENCIES are to be composed in the final application.

Figure 2.26. Composition of the sample commerce application with the added Basket feature, as well as the original list of featured products on the front page. Each class encapsulates its contents, and only the COMPOSITION ROOT has knowledge of all DEPENDENCIES.

The custom IControllerFactory creates instances of BasketController and HomeController by providing them with their respective DEPENDENCIES. The BasketService, for instance, uses the supplied BasketDiscountPolicy instance to apply a discount policy to the basket:

var discountedBasket = this.discountPolicy.Apply(b);

It has no inkling that in this case, the supplied BasketDiscountPolicy is an instance of RepositoryBasketDiscountPolicy that itself is a container of a DiscountRepository.

This expanded sample application serves as the basis for many of the code samples in the rest of the book.

2.4. Summary

It’s surprisingly easy to write tightly coupled code. Even when Mary set out with the express intent of writing a three-layer application, it turned into a largely monolithic piece of Spaghetti Code[13] (when we’re talking about layering, we call this Lasagna).

13 William J. Brown et al., AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis (New York: Wiley Computer Publishing, 1998), 119.

One of the many reasons that it’s so easy to write tightly coupled code is that both the language features and our tools already pull us in that direction. If we need a new instance of an object, we can use the new keyword, and if we don’t have a reference to the required assembly, Visual Studio makes it easy to add it.

However, every time we use the new keyword, we introduce a tight coupling.

The best way to minimize the use of new is to use the CONSTRUCTOR INJECTION design pattern whenever we need an instance of a DEPENDENCY. The second example in the chapter demonstrated how to re-implement Mary’s application by programming to ABSTRACTIONS instead of concrete classes.

CONSTRUCTOR INJECTION is an example of INVERSION OF CONTROL because we invert the control over DEPENDENCIES. Instead of creating instances with the new keyword, we delegate that responsibility to a third party. As we shall see in the next chapter, we call this place the COMPOSITION ROOT. This is where we compose all the loosely coupled classes into an application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.135.175