Enabling success with opinionated architecture

One of my pet peeve questions I often see on various tech mailing lists is “How can I prevent situation XYZ”.  In one recent case, it was “How can I prevent UI calls to mutable methods in the domain?”  The specific situation is one where I have two methods, with the same name, but in different layers:

public class Order
    public void AddOrderItem(Product product, int quantity)
        // Whatever

public class OrderService
    private readonly IOrderRepository _orderRepository;

    public OrderService(IOrderRepository orderRepository)
        _orderRepository = orderRepository;

    public void AddOrderItem(Order order, Product product, int quantity)
        order.AddOrderItem(product, quantity);

Now, if what we’re concerned about at this point is preventing developers from calling the “wrong” method, we’ve already lost.  If the method is available for us to call, it’s quite confusing, no matter who the developer is, on which method is the right one to call.

But why are there two methods available to us in the first place?

Instead of a tacit agreement in the development team on what method to call where, why don’t we just design our software so that when we’re developing, we fall into the pit of success?  When we’re designing or harvesting an architecture for our system, we’d like to ensure that not only do we have powerful seams for the ways our application deals with complexity, but our design is straightforward in dealing with everyday scenarios.

To do so, it’s paramount that your system’s architecture has strong opinions on the design of the system.  Strong opinions puts the design and development of the system on a set of greased rails, making it easy to travel in the direction you’ve designed it to travel.  If it’s difficult for the developer to travel in a different direction, either they’re going in the wrong direction or they’re on the wrong train.

Crafting and enforcing strong opinions

Instead of trying to enforce an opinion after the fact, we can design our architecture so you can’t get yourself into that situation in the first place.  The best prevention for a bad design is a good design.  It’s far too difficult and costly to try and clean up after months of bad design and try to enforce an opinion on a system that will fight it kicking and screaming.

Of course, we often can’t understand what opinions we want before we start developing.  Group and parallel design are key to narrowing down ideas, but one of the best ways to craft an opinionated design is through harvesting.  Instead of a Big Design Up Front (not to be confused with Just Enough Design Up Front), we’ll let the software tell us what the right direction is.  We wait until the Last Responsible Moment, where waiting any longer to make a decision would eliminate potential options because of the cost to retrofit.

FubuMVC is one great example of enforcing strong opinions.  In FubuMVC, it’s impossible to have anything but One View, One Model.  That’s the pit of success, where the designers recognized the benefits of having just one model and not a dictionary of values.  There are benefits to having the dictionary as well, but enforcing this opinion allowed more design optimizations around that direction.

Frameworks aren’t designed to enforce opinions.  Frameworks allow us to craft opinions, so let’s not assume the designers of a framework know how best to develop your system.

Going back to the first example, we don’t want developers calling the Domain methods in the Application layer.  A straight-forward solution there is to not expose the domain to the application layer.  Domain models get exposed to the application layer through projections or DTOs, and domain models get modified from the application layer through messages and services.  With that opinion enforced, it’s not even possible to call the “wrong” method.

If you want a certain design enforced in your system, the most efficient way to do so is to craft your design with strong opinions such that the only direction available is the direction you intended.  Not only do you enforce your design, but you can make optimizations such that traveling on those rails in that direction is as easy and fast as possible.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Design. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • “AltOxite” – Awesome!

    “Frameworks aren’t designed to enforce opinions. Frameworks allow us to craft opinions, so let’s not assume the designers of a framework know how best to develop your system.” – couldn’t agree more, great post.

  • lubos

    I was always pushing for “strongly opinionated software” (thanks for giving it a name) because it allows you to assume more about context in which your program is used and therefore give more output value to user for less input.

    But it’s risky strategy, you truly have to understand domain you’re developing tool for and choosing correct path is not an easy task, could take long time (many years) to tweak it correctly. Most developers I know will just design system in very flexible manner and will let users determine most optimal usage pattern over time.

    Most modern software works this way, that’s why we sometime talk about 80/20 rule when everybody seems to need only 20% of features but at the same time everybody demands different ones.

    Anyway, I like your blog posts lately, you’re now officially my most favorite blogger.

  • I recently did a post about Rhino.Igloo http://journalofasoftwaredev.wordpress.com/2008/12/13/using-rhino-igloo/ and because it is written with strong opinions it’s great to work with, you can work on the business concerns and let the plumbing sort itself out.

  • @lubos

    I should have put a caveat in there, that architecture and design are never “done”. We’ve changed our opinions in the software, but that’s still rare. More often, we refine them over time.

  • I very much agree with what you are saying here. I have a practical question about your example.

    It’s all the rage now to trade layering — using separate assemblies to enforce loose coupling — in favor of IoC combined with good design. My team has embraced this approach in our latest app. Also, in this app we use DTOs exclusively in the UI. The controllers speak only DTO to a business service layer.

    However the service layer and domain are in the same assembly, along with repositories too.

    You see the problem here. Right now we are a small team. I do worry about the “dumb programmer” who comes along one day and, ignoring the very clear DTO pattern, starts instantiating domain entities in the UI, referencing repositories in controllers, yada yada.

    Proponents of the fewer assemblies approach say I should deal with this as a “team smell” not an “architecture smell.” I agree. My question is this. Does this approach conflict with your “pit of success” through architecture idea?