Are there any obvious flaws to this OO architectural model which I intend to implement using javascript? It is similar to the MVP model but instead the role of the model is broken down into three modules:
- the cache (stores data instead of rerequesting from server)
- the model (manages requests for raw data, retrieving it from the cache if available or the server)
- the modifier (handles requests which require data which makes changes to the data on the server)
For a diagram showing how data flows through this model:
view
- sets up & manages the UI possibly requiring the requesting of data from the presenter
- sets up controls to alter what is displayed, possibly requiring the requesting of data from the presenter
cache
- contains a record of some fetched data in data objects
- has a .store() method for storing data in the cache
- has a .retrieve() method for retrieving data from the cache
- access for storage and retrieval is available to the model and the modifier (acting as a faster intermediate location for the sharing of information than the server)
presenter
- handles requests for formatted data from view
- requests data from Model (passing a callback for completion) formats data for presentation and calls view callbacks
model
- handles requests for data from presenter
- requests data from cache (cache.retrieve())
- if data not in cache, requests it from server and then stores it in cache (cache.store())
- data passed back to presenter via callbacks
modifier
- handles requests from view for modifications to data
- specify post, put and delete request types
- only support specific types of request which may be handled separately (eg settings)
- stores new data in cache (cache.store())
- pushes data to the server
- calls callback about the success of data storage
This is quite big project and there will be a number of others working on it so it would be good to get it right from the off.
edit 1
The cache.store() and cache.retrieve() methods both only receive the information required to refer to the data in the cache, but do not specify how it is stored. The cache methods use this information to respond with or store the correct data, but the internal structure in which the data is stored is kept secret.
Alright, you’re editing as you go so let’s walk through what I’m currently seeing:
So a user selects something in a combo box triggering behavior to fill in the contents of another combo it’s connected to.
The view requests data from the presenter with an ID(s) for the data and something identifying the combo needing to have its contents refreshed so the presenter knows who’s asking so it can set up the callback to give the formatted data to the right object.
The presenter requests data from the model with the ID(s) for the data and a callback for formatting that data.
The model requests data from the cache with the ID(s) for the data. It either immediately returns data it already has or it requests data from the modifier with the ID(s) for the data. It ultimately gets that data and fires the callback with the data as an arg which ultimately formats and hands it to the widget that needed it.
The big question for me… Why are you passing the same argument 3-4 times before anything actually happens? That should smell bad to you. Very bad. Any time you pass the same arg twice in a non-currying type scenario, you should take a whiff. If two objects need to act on the same argument, they should exist as components of another object.
Why does anybody but the combo view need to know what other combo needs the formatted data? Why should anything outside of the combo view know how it’s supposed to format data for the view? Why should more than one top-level object be handed the ID to ultimately get the data?
As I said earlier:
I think you’re breaking out subdomains without really needing to.
Model makes sense with cache and modifier inside it. Nothing else need be bothered with the details of how those two work and that general model object is also a good place to have helpful methods for adding new types of data templates containing all the data the model components need to implement the new structure and make it accessible/bindable.
Presenter is definitely a subdomain of your view. Converting data to a view-format is one of the primary responsibilities of any view. From a portability perspective this will always need to move with whatever it is that’s interacting with HTML in other ways so there’s not much point in separating it.
At the highest level I tend to see these three as the evergreen concerns worthy of separating in any non-trivial UI app. There is of course plenty of other stuff you might want to separate for a given architecture. I just see these three as somewhat critical if you’re talking about a highly re-usable client-side architecture/framework.
-
Data-structure/server-communication – Imagine you want to pull a white-label-ish make-over on another similar app of a recently acquired/merged company and you can mess with everything except for the requests that ultimately update and retrieve data from the new server whose code is a complete disaster and better left untouched for now. This is where you need a generic interface for updating, binding, and retrieving data that’s decoupled from how that data is actually structured and sent to/from the server. Whatever the ajax is doing, however the data is delivered and sent and looks at point of entry, interacting with it should work the same for combo box UI app logic in the new app as it does for interacting with data in the original app. In your case, I’d probably canonicalize the new data to look like the old data at point of entry and then adapt at point of ajax interaction so you can keep using your old caching logic.
-
UI App Logic – This is the layer that buries the DOM and HTML stuff. A server-side dev with minimal client-side expertise should be able to figure out how to implement a widget with this. A controller basically, but I prefer implementing as another event-driven layer whose plain-english non-ui-dev-friendly DOM-agnostic events are triggered from the UI layer so we can leave it intact and just swap the HTML/canvas/svg/whatever layer out under the hood. Here we set listeners like
comboObject.newChoice( handler );
so some poor bastard pinch-hitting from the server-side can handle basic implementation if needed and we have clean separation of data and environment concerns. Unless you’re adding brand new types of functionality, this should never need to change once it’s done and it should be relatively easy to write self-documenting code here. -
UI View logic – Are we building a combo box in canvas or HTML? This is the thing we’d swap out to preserve everything else in the app for re-use in a new environment like if we wanted to make a tablet mobile app version of an existing desktop oriented site with drastically different layout but serving the same business concerns. Anything that formats data for HTML or sets events for daisy-chaining from DOM to the UI app logic layer lives in here. Nobody should see jQuery anywhere but here unless you’re using it’s event system for non-dom concerns. It might make sense to have a generic Presenter-like object wrapper in here that you wrap data in for convenience methods that do things like generically convert data structures to lists and tables in HTML format as well as bind data changes to behavior for the widget.
There are a few flaws in the design, in my opinion.
-
Both the model and the modifier need to know how the data is structured internally. This will likely result in a duplication of code and/or responsibilities.
<update>
For example, you have a Customer object who can hold an Account. Now the model needs to know how Customer and Account are related so the correct information can be retrieved from the database. But the modifier needs to have the same knowledge to be able to perform updates of the data. If there is now a change to allow multiple Accounts per Customer, there are two places where this change has to be applied. This is the kind of duplication I refer to.
</update> - I would have made the interactions with the cache more transparent.
All in all, I would make these changes to the architecture:
- The modifier makes the modifications to the model instead of the cache and the database. This gives the modifier the same responsibilities as the Controller in the MVC pattern. You might consider renaming the modifier accordingly.
- Replace the cache with a cachedDatabase, which handles the database connection and caches the results for fast access the second time the data is requested. For the model it should be transparent where the data came from (cache or database).
6