Rich internet applications bring a powerful user experience. But in order to achieve that, they must be written totally different from traditional webs. The paradigm is totally different and so the architecture. There is no “server side” as such. In some cases it’s just a deployment site, where you have to browse in order to load the app. In some other cases you also need a centralized site to:
- Initializing the application (deployment).
- Storing data to be shared among instances/users (persistence).
- Connecting instances/users to each other (communication).
Rather than calling it “server side”, let’s call it the hub. Its architecture must contemplate mainly:
- Security
- Concurrency
- Scalability
But everything else, lives in the application. The business domain, lives in the desktop application (which is inside the browser). There might be some security constraints related to the business that need to be tackled by the hub. In those cases, that particular bit of the business lives in the hub.
In order to be productive, the hub must understand JSON very well (assuming the data is sent/loaded via JSON) and serialize/deserialize it to objects automatically, without the need for hand-written mappings. This is one of the reasons why using Node.js in the backend makes sense, because you don’t have to rewrite your objects in two different languages. Anyway, I am using C# and Python as the backend languages in two of my projects and the idea is the same.
If there are no particular security constraints, hub must accept data sent from the application and store it without objection. The best way to avoid objections is using schemaless databases like Mongo.
If there are no particular security constraints, hub must accept data sent from the application and store it without objection. The best way to avoid objections is using schemaless databases like Mongo.
Fortunately C# with ASP.Net is fantastic for JSON serialization/deserialization. For Python we’ve had to write code ourselves to get some automated conversion working.
As the hub is not the core, its functional requirements emerge from the incremental design. Such a design starts in the application side which eventually might need to send or retrieve some data to the hub. So my Cucumber scenarios, talk to JavaScript in the application side, specially if they are end to end.
Examples:
– Adding a “comment” to a “merchant” (where merchant is an entity, and comment is a value object): is done by the application. The hub just stores the entity as sent by the app. It might have a security check to make sure the logged user can make changes in the merchant. If there is any additional specific security constraint saying that comments can be made only by certain users, then we need to check that in the hub too, for the sake of data consistency. But that validation would go into the security rule’s code. The hub’s “merchant service/repository” won’t know anything about comments.
– When listing merchants, show only those from a particular country: is done in the hub. If the security constraint says that logged users from the UK can only work with merchants from the UK, we can’t send all the merchants to the app for it to filter them out. Instead, the hub makes sure that only the information that can be seen in the app is sent.
So for rich internet applications, the hub is there just to support the architecture. Don’t start the implementation of a feature in the hub, because you don’t know if you are going to need it. Keep your hub layer as thin as possible, adding complexity only when proven necessary.
This will be probably a chapter in my upcoming book where I will explain it in more detail.
The rationale behind:
The reason why desktop applications provide much better user experience than server-side web apps, is because they react immediately to user input. They interact in real time. When the user asks the application to save some data, the app should immediately tell the user data is saved, even if it hasn’t got confirmation from the hub. If the hub doesn’t respond or informs about a problem storing the data, then the application should inform the user, handling that somehow. But it should not make the user wait. So the application must have all the business knowledge to implement this solution.
Imagine a desktop application, a text editor for example. Now imagine it runs in the browser. All you need the hub for, is to load the application. If you want to access your text documents from different machines, then you need a hub to store and serve that information. Why do you need any business logic in the hub? What for?
In a later version, we decide to implement a new feature: collaboration with other users. There might be security restrictions. You might not want to have all the users opening your documents, but just some. Then you go and add a filter in the hub.
So it’s not that you design some kind of service in the hub first and then call it from the application. It’s the other way around, like in TDD, I might write a call to a centralized service that doesn’t exist yet, once I know I need it in my application and once I know how I want it to behave. Then I add that behavior in the hub if necessary.
Eventually if the interaction among users is very complex, the hub will become complex and will contain important business logic, but its design will emerge according to the needs of the application and not the other way around.