Modeling - Ultimate Way of Architecting Software
While architecting software, I found modeling approach very attractive and promising.
Before I delve into details, let's stop for a second and try to build correct associations - between word "model" and our imagination. Model, to me, is something that demonstrates the purpose of the product, whatever that is. It's nothing difficult, just that.
Please don't confuse model with prototype, as prototype is something functional that demonstrates a working concept; prototype is a simplistic implementation of a product for the purpose of PoC (Proof of Concept). Model, on the other hand, can serve as a full-fledged basis for the completely functional product.
Model is Natural
First characteristic of a properly built model is being natural. I can't stress enough the importance of this part.
Many software architects think of a model as something way too complicated, as if they were proving their intellectual dominance to the organizations they are working for. In my opinion, those architects are missing the point.
To build a model, you don't need special knowledge other than what you are trying to build and to model. Nouns in the organization's domain build up the entities (classes) of the software model naturally. Verbs in the problem domain define entity's operations (methods) and relationships between them (dependencies, references, cross-type calls, and event listeners).
Model is Platform Independent
Second characteristic of a properly built model is being platform independent.
Frustratingly many software architects cannot define clear boundaries between platform independent and platform specific parts of the model. Eventually, they end up building platform specific implementations of their initial ideas (or the ideas were platform specific from the beginning - which is worse). In the early days of the product development they refer to this as a very nature of the product they build, and they underscore the needless efforts to support platform independence. Whenever platform specific implementation needs to be replaced, they call it a technical debt and dedicate another century to rebuild things a little bit. So, I don't think the final product they are building I would call a quality software - it simply does not pass the criteria.
Platform seems to be over-rated thing that does not allow technical architects think in a simpler way. My recommendation is to forget about platform and technologies; go back to the roots and build things that work the way you understand the domain, not the way you understand technologies. Then, go and figure out how to implement this model with the given technologies. Make technologies fit into model, not the other way around (please).
Model is Unit-Testable
Third characteristic of a properly built model is being unit-testable.
This seems to be an outcome of the second characteristic (being platform independent), since platform independence is the cause for having enough level of abstractions for the later substitutions using platform specific implementations. Since same abstraction can be implemented by using mocks, the code automatically becomes unit-testable as well.
Unfortunately, many people started thinking about unit-testing as something easily achievable by using shims. And, also unfortunately, there are plenty of shim frameworks out there that promote tight coupling with the platform specific code. I would not call a code unit-testable if the only way to test it is to create shims.
For clarity, by "shims" I mean platform specific mocks that on-the-fly replace the functionality of the platform or particular technology that your code refers. For example, if your code refers SharePoint object model, shims would be used to mock out the SharePoint framework to act as if it was a mock, not a real SharePoint implementation.
Main reason that I discourage unit-testing using shims is because you will start creating more platform specific code instead of clear, platform independent models. And later on, you will face the challenges of refactoring, which I mentioned above.
Chat Model Example
To demonstrate the parts that I already covered above, let's consider an example of a chat room that I want to build (diagram is provided below).
First of all, for the sake of being natural, I identified nouns in the domain I'm targeting, and these are ChatRoom and Participant. Relationships between them are quite obvious - ChatRoom manages list of Participants, by giving them a way to send and receive messages. Participant can send to and receive messages from ChatRoom.
Next, for the sake of being platform independent, I'm not even thinking about the implementation yet. Let's say that the customer is not sure yet, it can be either a windows application, or a web application. But for now, we don't know and so we don't care.
Finally, for the sake of being unit-testable, I kept Participant abstract along with its ReceiveMessage operation. So, by substituting mocks of Participant into the ChatRoom, I can see if it manages all the participants properly (delivers messages to the correct recipients).
Now, let's talk about the platform specific part of the topic.
First of all, platform or technology is just an implementation detail, so never worry about it earlier than it's required. As I mentioned above, your goal should be to fit technologies into the model, not the opposite.
I've seen many architects blindly following technology and platform, by promoting very specific ways of modeling that fits only one particular platform or technology. Those architects, to me, are developers who have mastered the technology and cannot think out of the [technology's] box.
Next thing about implementation is the ability to be pluggable. Your platform specific code should construct a model by plugging platform specific implementations into it; thus making it a platform specific instance of the platform independent model.
Platform-Specific Chat Implementation
Now, about our chatting software - the customer finally decided that both web and windows clients need to be supported. The ChatRoom instance must be kept alive on the server as an executable (service or running process).
As you would imagine and I would expect, I'm not going to change my model anyhow. I am going to figure out how to fit all these requirements into the implementation (platform specific) part of my software.
So, here is what I'm proposing to the customer (diagram comes below):
For windows-based client application, I will instantiate the ChatRoom class (inside WinChatRoomClient) that will manage two types of chat participants - WinParticipantClient and WinParticipantProxy. WinParticipantClient is the representation of the currently running session, opened by the person who runs windows application. WinParticipantProxy is a representation of the other participants that are present in the chat room. We have these two implementations because they handle message send/receive operations differently. WinParticipantClient is responsible for displaying received messages on the screen. WinParticipantProxy is responsible for sending the message over the wire. Communication between them still happens through the ChatRoom instance, which comes right from the model's code.
Very similar approach is used for implementing web-based client. The only difference is that there is no two-way communication available between the service and the web client, so the database is introduced as an intermediary transport mechanism for exchanging messages. Web client is a single web application that serves all clients.
On the service-side we have one more (final) instance of the ChatRoom class that holds service-side implementations of the Participant - WinParticipantHost and WebParticipantHost. This is needed to support the differences in message exchange approaches for windows and web clients.
Here are proofs of concept for my approach:
- Platform is treated just as implementation detail: no change to the model whatsoever. I made my technology needs (service, windows and web screens) fit my model, not the other way around.
- Platform specific things are kept pluggable: you can refer to the client classes (ChatRoomHost, WinChatRoomClient, WebChatRoomClient) on my diagram that describe how specific implementations are plugged into the model (through notes attached to each class in the diagram).
So, to sum up the above story: modeling, as it is described here, is the right way of architecting software.