Showing posts with label DDD. Show all posts
Showing posts with label DDD. Show all posts

Friday, March 4, 2011

Stardate 2011.63.1: Design stenches - Rigidity

A lot of people are aware of what others call design smells. I hate using the term smell, so I use the term stenches, but they amount to the same thing: Something that reeks up the place somethin' fierce. I'm going to be writing a series of posts with my experiences on projects that feature plenty of malodorous designs, and show how I would have improved them and why they are better. I hope to keep these relatively short, as I tend to go too long, but if you have questions, I can answer them in follow-up posts.

From Agile Principles, Patterns, and Practices by Robert C. Martin, rigidity of design is defined as:
"Rigidity is the tendency for software to be difficult to change, even in simple ways. A design is rigid if a single change causes a cascade of subsequent changes in dependent modules. The more modules that must be changed, the more rigid the design."

From my personal experience, this happens most often when people are more worried about data than they are about functionality. For example, for a simple property bag, if you make an interface for it and make more than one implementation, what happens when you change the interface because it turns out it didn't have all the data you needed, or the datatype on one of the properties needed to change? Did you have control over all the implementors, and did you just publish an unnecessary breaking change? For this reason, I do not support having many properties on an interface - 2-3 tops, preferably none.

In any case, when you change the interface, it completely screws the app. *Every* place that uses that interface now may have to change, depending on the type of change. Does this mean we shouldn't use interfaces? No! We should only use them when they are appropriate, and ensure that they encapsulate a responsibility, rather than represent a piece of data.

To fix this, I would only put the appropriate functionality for a given class on the interface. More importantly, I try to ensure that the interface is truly stable. Without stability, interfaces are actually less useful than standard classes.

Wednesday, January 19, 2011

Stardate 2011.19.1: Domain Events - Powerful, Stupidly Named

Today, I thought I'd point out something that is terrifically obvious: Domain events may be the strongest concept to come to domain-driven programming since the ubiquitous language. I almost always have an object of type DomainEvents these days because they're just so useful. They become a kind of repository for the different things that can happen in the domain that really have no other place - see Udi Dahan's website for more information about the concept of domain events.

Also, this is probably the worst name for a type in the history of mankind, and might be a mistake to have the object at all. The reason for this is that, like my rant against Commons projects, it often becomes a kind of repository for trash that just needs to be maintained over time. There are sometimes Action<T>s in it, which is a mistake, but also it is often a static class that just has the different possible domain events in it that can be raised statically. This makes it difficult to test, but there's also something more obvious: There's virtually no ubiquitous language which contains the concept of an explicit DomainEvents object in it. 

This is pretty obvious, however, and does not take away at all from the usefulness of the concept. Often, we require a place in the domain where we can just do different things that aren't necessarily part of the normal flow of execution of the business domain. For example, when filing a legislative bill, there are a crapton of things that can happen before the bill even gets into the first committee. However, there's no explicit, "Here's what's going to happen every time!" You can't necessarily model it effectively as a function that follows another function, e.g. if you have a SendToCommittee function, you can't necessarily prefix it with all of the functions that may happen before it goes to committee - there are just too many possibilities!

So how do you handle it? The best way (in my opinion) is basically with an event. There are many different ways to implement events in most programming languages. Since I'm a .NET developer exclusively, I'm going to talk about developing domain events in .NET, and C# in particular. I have no fuss with VB development, nor VB developers, but I'm just not familiar enough with it to be able to talk about it intelligently.

The standard DDD way of handling domain events in .NET uses the weak-eventing pattern. This is a pattern where you have a list of WeakReferences to actions. These actions get executed if they're valid references, or are skipped if they are not. That then becomes your domain event. It has to be a list of WeakReferences due to technical issues with subscribing causing a hard reference to stick around when it probably shouldn't - perhaps because the objects that have hooked up to the event would otherwise have gone out of scope.

My next post will have more useful content. I just wanted to point out that having an explicit type called DomainEvents is probably not the right way to go. In the next post, I'll discuss how I typically handle it, and why I think it's stronger and better OO design.

Tuesday, January 11, 2011

Stardate 2011.11.1: Interface Segregation Principle vs Common Sense

The Interface Segregation Principle is an incredibly useful and powerful principle of software design. The ISP basically says that you should keep relatively lightweight interfaces so that when a person implements them in order to hook into your system, they do not need to implement tons of methods that aren't relevant to their class. I usually try to have no more than five functions on an interface - that's just a rule of thumb, but I find it to be a useful one.

But it's also very important to remember that, like all principles, it can be taken too far and make your life a living hell.

In particular, the interfaces should still be large enough to define the appropriate functionality that is being replaced. If you go further than that, putting each function and/or property into its own interface, then you have the opposite problem from the ISP occurring: Implementers who want to change how your system works will likely not be able to tell which interfaces have to be implemented in order to provide a given functionality. This is especially true if you've shunned type-safety and used object/dynamic throughout your system.

Some people try to solve the above problem by building up their interfaces into one unified interface that has all of the functions/properties required. This leads to other problems, though. When I decide to use an interface, I always ask myself this question: Do I have some way in the system (at all) to change where/how that interface is instantiated, and do I have a reference explicitly to that interface anywhere that would make switching the underlying implementation useful? If that point of extensibility doesn't exist, then having an interface around it is actually not really that good of an idea. With that in mind, if you have no way to actually change which underlying implementation is used, or if you don't even have any references to an interface except in an inheritance chain, I think we can safely say that that interface is likely unnecessary.

What do you think? Do you agree? Leave some feedback.

Monday, January 10, 2011

Stardate 2011.10.1: Revenge of the Commons

In my day, I've seen a lot of shoddy architectures. From a COM system that read data as strings from the user-interface, converted it to its inherent datatypes, then turned it back into a string and sent it from the client to the server, then read it as a string and turned it into its datatypes, did work on it and sent it back, the entire program of which was written in about 200 3,000-line switch cases (copy-pasted to the server and the client), to a project that only had a few successful continuous-integration builds for the entirety of its lifespan due to such a low-quality codebase, and such immature developers.

Today, I'd like to discuss the Commons; not Boston Commons, but rather Commons projects. These are typically defined to be a series of interfaces, classes, and user controls that didn't seem to have an obvious spot in the domain. They often represent purely technical concerns that have little to do with any real business functionality, such as a combo-box that automatically formats its input in various ways: There's no obvious place that that really fits into the domain, so why not throw it into the junk drawer?

Wait, junk drawer? Is that really all Commons projects are? It seems so. While I understand the utility of having a Big Ball of Mud for things like this, they really do tend to cause significant issues later on down the road. The reason they do this is because they tend to have insanely high afferent coupling. In other words, there are a lot of projects which depend on some or all the classes in the Commons project, and so when another project needs a special version of the class in Commons, and they decide to change that class directly because it fits in the other places that they're aware of, it breaks all the places that work with that class because they didn't realize they had to fix it, and didn't bother to check it (whether because they didn't know they needed to, or because their test-cases weren't well-defined).

A very high afferent coupling doesn't necessarily mean it's a terrible architecture, though - it just means that it's very likely to have problems when those classes need to be changed or fixed, and so the overall design quality is likely lower. The main reason for this is that Commons projects tend to be highly unstable. Consider, for example, Core projects - projects which are decided to be core either to the domain or to the architecture. These projects are usually extremely abstract and tend to be more stable than any Commons projects, because the Core projects define the interfaces and abstract classes which are in-turn implemented specifically in the modules. Since they are abstract, there is a kind of implicit understanding that they should be stable within the domain, i.e. they aren't going to be changing much for any reason that the business people might offer, and at the same time should be relatively stable within the architecture.

Does that mean we should make the Commons projects more stable and abstract? I would say we should certainly make them stabler, but it would be incredibly difficult to make them abstract. Often-times, they actually have abstract components buried in them, but those abstractions are not at all stable. I think better is to avoid, if at all possible, Commons projects altogether, and instead look for ways to embed these pieces into the modules themselves. I would even go so far as to say it's "OK" to have copies of each class that seems to be common distributed throughout the modules. Whoa whoa whoa - what about the DRY principle - don't repeat yourself? Well as it turns out, when DRY could potentially cause you a lot more work due to one class handling several different use-case scenarios, DRY should take a back-seat to the SRP - the single responsibility principle. In that case, the SRP allows each of the different classes to serve their respective modules without breaking the other modules' implementations when bug-fixes/domain changes/architecture changes are required in those particular modules.

I certainly understand that that leaves a kind of gritty after-taste in your brain when you have to do it, but in my experience, it is simply much, much easier to work with/maintain this kind of system than it is one with multiple Commons projects that serve every module individually. "Find and replace" is still better than "Make one class dangerously handle every responsibility."

Thursday, January 6, 2011

Stardate 2011.6: Interesting Exercises

Lately, I've been delving a lot into DDD, or domain-driven design. I find it to be the single most elegant way to tackle really complicated systems. Some people say it's a lot of work, especially just for small apps, but I find it to be completely natural. For example, on a previous job, the users needed a batch publishing system that would loop over a set of Word documents, convert them to HTML, and then save them to a directory which already had publishing features turned on, so that when HTML files were put into it, they'd be served appropriately up to the web. I didn't have time to write more than the bare necessities, but since every class and functional piece of code was named in such a way that the business user understood the meaning of the pieces (for example, I used an IFilingCabinet interface for the documents, which they used all the time of course) and they were able to take it and add to it so that even though they started with the barest essentials, they ended up with something really powerful.

I think it would be interesting for a book on DDD to propose exercises. These could be considered to be a kind of kata for DDD. As such, I'm going to begin publishing exercises that I've thought of, from the very simple kinds of things you might find useful around the house, to complicated, industry-standard systems that are extraordinarily painful and convoluted.

The first kata I propose for DDD is rather childish, but comes from a joke I made in an email the other day.

Name: Mouse Trap Simulation
Description: The idea behind this kata is to write a simple simulation for how a mouse trap should work. There are many variants of mouse traps.

  1. The standard spring-loaded bar mouse trap (with cheese)
  2. Glue paper
  3. Poisonous foods
Let's just consider those three. What are the pieces that make up every mouse trap? How can you write the code so that a mouse trap entrepreneur could come in and extend your system without having to change the original code? What does your test harness look like - does it have a human setting the trap, a random mouse encounter?

One thing to note about it is that the requirements are not well-defined. Feel free to ask questions in the comments!


Goal: The goal of this exercise is to develop a simulated home/work environment where rats may be an issue. The code should be structured in such a way that when a normal person reads it, they can have some idea of how the system works. They shouldn't need to be programmers - for example, they might just be business analysts who dislike rats. The key to this exercise is creating a truly decoupled design that allows every mouse trap to do their own work, without making it difficult for an extender of the system to implement that work. If your base classes/interfaces make it difficult to extend them, then You're Doing It All Wrong ®.

Bonus points: For extra points, design your *test harness* to be extensible as well, because what if a trap can't truly be tested in the same way that other traps can be?