Software is like an Oger. It’s big, nasty, angry, rude, and has layers. In fact, have you ever heard anyone say, “Heck, no. I don’t want any software layers?”. Everybody loves layers!
By layers, I’m referring to a means of getting the one way ordering of dependencies and the separation of bounded contexts as a means of wrestling our code in to a workable and maintainable shape. If you want more, read the ‘teal’ book by Eric Evans. He does describe it in some detail.
For those not interested in Eric’s work, the idea is you group your application’s code by which ‘tier’ it belongs. Is this code for the ui? Is this code for persistence? Is this code for something else?
Like a linked list, each layer is only connected to layer directly below. The problem then becomes where to draw the lines. Sometimes it is clear. The UI shouldn’t know about a cursor, should it? Other times it isn’t so clear, like your core data model. Does it live outside the layer cake, or does it go all the way to the bottom?
Scalability can be gained using layers. The db layer can be stored procedures, the business tier can be we services, and the UI can be ASP.NET. Scaling is then a matter of determining how much of each tier you need. I’m simplifying a bit, but the decomposition helps with those decisions.
It also helps isolate changes. If we move from ASP.NET to Rails, I can still call all my web services and not impact the rest of the code. Conversely, I can replace the .NET web services with Ruby’s Sinatra, Erlang’s Mochiweb, or Haskell’s Yesode. Who would notice? Other than my team, who would be pissed that their BL is now “unmaintainable” since I’m a huge nerd and picked languages and frameworks I’ve not bothered to teach them yet. Actually, let’s be honest. Some of them I don’t know.
But what of your UI and Persistence are tightly coupled to your platform? Let us say, for the sake of this argument, I’m writing an Android application. The UI is defined by the platform. The storage is defined by the platform. So any layered business logic is transitively coupled to the UI. So much for one way dependencies…
This isn’t specific to Android. It’s iOS and WIn phone as well. Android just makes it easier for me to put on my pointy-haired-boss wig and ask, “Can I reuse that code in my Java ESB Tomcats Netbeans? Or do you have to port it to Eclipse?” He doesn’t care about tech. He just wants to save time and deliver value.
So let’s start by looking at what the layers look like.
Can we change the directions of the arrows? So the business logic is independent of the, formerly, lower layers? Then acknowledge our platform coupling? Then isolate our wrapping code?
This is the idea behind the “Ports and Adapters” model. I like it better than layers because it acknowledges something. That your bottom layer is sometimes coupled to your top so it’s artificial anyway.
Now we have our business code with no external dependencies. It can move from place to place. We can replace the storage engine, or the UI engine. As part of this, I can replace the UI or storage with stubs, mocks, and other test doubles. Which means I can cover all the really important stuff in my tests. In more practical terms, I can have my Android Activities be thin wrappers that inject dependencies in to my business code.
Want a real-world example of a win? I was reviewing a coworker’s code that was untestable because it called
MessageBox.Show(...) and we decided we just needed to wrap those calls with an interface that asked the user a yes or no question. The implementation then uses MessageBox, but the unit tests can inject an implementation that response yes without hitting the UI.
The ports and adapters allows a lot of the decisions that would be handled in code touching the UI, and therefor difficult to test, to be decoupled from the UI, the database, and even the underlying file system.