The Quest For The Perfect Project
Thursday, 1 March 2007
Of Layers and Designs
Steven Nagy pointed out that we hadn't said what we meant when we mentioned that Emma was working on the Domain objects. This post will hopefully correct this, give an idea of the design we'll be following, and provide a good opportunity for those of you who are really hot on n-tier programming to not only rubbish the design, but also my description of n-tier (and "n-tier plus domain"; I don't know of an official name for it) designs.
Traditional 3-tier design
The traditional 3 tiers are the UI layer, the "business object" layer (BOL) and the "data access layer" (DAL). Here's a graphical representation:
Each layer only "knows about" itself and the layer directly beneath it - so the UI layer doesn't have direct access to the DAL. That way, the theory goes, the DAL can change completely, and only the BOL should care. Although I haven't done any "straight" business applications, in my experience things tend to leak a bit - the nature of the DAL affects the interfaces the BOL exposes, so radical changes to the DAL may well cause a ripple effect. In addition, because the UI and the DAL have nothing in common, you can end up with a lot of duplication, where very similar data structures exist in the BOL for the UI/BOL to manipulate, and in the DAL for the BOL/DAL to manipulate. Removing this duplication leads to what I think of as a "n-tier plus domain" design, or possibly "n+1"-tier.
3-tier plus domain design
Here we still have separation of the UI, business objects, and data access, but there are two kinds of business objects involved: domain objects, which tend to have the actual data (so in an online shopping system, domain objects may be customers, items available for purchase, etc) and service objects which are able to do things such as searching (via the DAL). Sometimes the boundary may not be very clear - should the shopping basket class know how to commit itself (in terms of the order being submitted) or should that be a service? No doubt different people have different views, and I'm not going to give one here. Anyway, the important difference between this design and a normal, strictly vertical n-tier design is that all layers have access to the domain objects. That means the UI can create (say) a shopping basket, and ask the business service layer to store it for the duration of the session. The business service layer can then (after applying appropriate business rules) pass the same object to the data access layer for actual persistence. That means less repetition, and is particularly important when using an ORM (Object Relational Mapping) system (where frankly, it can be quite tricky sometimes to work out whether a class is part of the business layer or the data layer, sometimes). Here's a diagram illustrating this design:
This approach, or at least one approximating it, has worked pretty well in a couple of projects I've worked on, and I believe it's gaining favour in the world in general. Now, what about the Battleships project itself?
Battleships doesn't really fit into a BOL/DAL design at all, so rather than trying to squeeze a square peg into a round hole, we're not even going to try to claim it's a normal n-tier design. However, the idea of having different layers which all have their own jobs and have commonality in terms of domain objects. With the caveat that we haven't actually written any of the code yet or even designed the interfaces, here's our current idea of the overall design/architecture:
There will always be a server involved, even in a straight two player game, even if we ever support two players on the same client, taking turns having looked away while setting up the board. What's not clear is whether a communications layer is always required. It's possible that the interface between the server and the communications layer is the same as between the UI layer and the communications layer, in which case when the client is in the same process (and application domain) as the server, there's no need for an extra layer of code at all. It's likely that the arrows going "up" in the diagram will actually just be in terms of events: the server may well supply an interface which includes various methods to be called (e.g. for the player to make a move) and events to be handled by the layer above (e.g. player X has moved; it's now the turn of player Y). There's not very much difference between that and the communications layer passing in an implementation of an interface for the server to call at appropriate times, but it makes it easier to add logging/debugging hooks etc with the normal .NET eventing model.
Quite where the MVC/MVP (Model View Controller, Model View Presenter) patterns come into this remains to be seen. (One aim is to investigate MVP thoroughly enough to know how appropriate it is to this game.) The UI layer will need to maintain a model of some description, partly comprised of the common domain objects, and partly comprised of its own view of things. Likewise the server's model won't be entirely comprised of common objects. This is because the server needs to know everything, whereas we're taking a paranoid approach so that clients only know as much as they need to, rather than having a complete view of all game boards.
Separating out the communications layer from the UI layer is not only an obvious thing to do, but it should allow us to play around with different technologies pretty easily. We can try a "straight sockets" comms layer with our own protocol, or various different types of web service (SOAP, Hessian etc), or different .NET remoting technologies ("vanilla" remoting and WCF). We could even go exotic and implement an email comms layer - it should just work (although obviously the lag would be horrible). The comms layer is likely to break down into two halves in most cases - the client side (e.g. the player's computer making web requests to either do something or poll for events) and the server side (e.g. responding to the web requests). How exactly this split happens is likely to depend on the comms layer in question though - I don't think there's any need to standardise there.
Similarly, lots of different UI layer should be possible without too much work. We may find it easiest to write a small console UI first, which after each turn just displays "our" board and how damaged we are, and how we've done against the opponent's board so far. Very little work involved in that, even if it would look ghastly. We can then move on to WPF, WinForms or even UIs on other platforms.
It's probably that many UIs will want the same model code, so we may well break the UI layer up into "genuinely presentation-specific" and "generic UI model". I don't see that as another vertical layer, so much as breaking up the single UI layer into two (or more) parts.
Hopefully that's explained a bit of what we're trying to do, as well as my understanding of a couple of architectures for traditional business applications. I'm sure many people interpret the architectures in different ways, and bend them to whatever they happen to be doing - which is fine. Having said that, I hope I haven't misrepresented them too badly!
Having written up the architecture, fairly soon I think I need to start putting fingers to keyboard and actually working out what the interfaces between the layers look like... more on that when it happens.