This post belongs to a broader mini-series on how we do Single Page Apps. So I advice to have a quick look there to know context a bit
You sure do remember days of starting new project with coolest, modern MVC framework in your favourite language, and picking your favourite ORM you could die for evangelizing, and hating all other database systems that are different than the one you worship?
Well, you are wrong then. .. So did I.
We must remember, it all ends up with an app that user uses via browser and gives a fuck to all the technology we use underneath.
Users want a working thing (a game, an app, a website, a crm, intranet app, you name it..) that is:
- responsive (aka Single Page Application)
- does what they need (aka early prototyping, and having short feedback loop)
- they can change it (aka change of requirements)
- error free (aka show me your code)
- error free (aka maintainable)
- multiplatform (aka shared codebase)
But – always trying to write readable backend code.
Some of you may have already read Uncle Bob’s Clean Architecture blog post on separating layers.
You’ll find similarities in GameBoxed styled architecture on how we do it.
In the system core there are usecases.
There can be many usecases, implementing some fraction of business domain. They will mostly use rather dumb objects (you could say your model).
When you look there, you should see clearly what is the app purpose and behaviour, flow of user scenarios.
So let’s jump straight to our code, and see for ourselves.
To repeat: pure domain. You can test it in command line. You can run it in the browser environment. You could run it in any backend supporting JS if you’d like.
Give it some input (load data), use it and work with its output. May be as the BLACK BOX for you.
Usecases have three types of methods
- Input: entry points to accept end-user requests (we name them with words from business domain, but these requests come from naive clicks, or text inputs, or finger swipes, or whatever else a user can do to our app) “playerFinishedTutorial()”
- Output: empty methods that are results of some important logic happened. You can think of it as events too. “readyToStart(), nextSecondElapsed(), playerGotPoints()”
- private methods of a usecase
Let’s see one that requests something from a usecase. A simple one this time:
Data of a usecase lives in browser’s memory. It’s a live data structure (a state machine one could say) that changes only because of a user request, or when some time countdown completes. So when it changes, it communicates it … ehmm.. on itself (you’ll see why later)
No domain method knows about view, nor communicates with it directly!.
To connect output of a usecase to external services, view, sounds we are applying a thing called “glue”.
We glue 2 layers together: domain and view. In both directions. It’s thin layer! Knows how to pass parameters. We do it with simple AOP.
This way an internal layer (domain) can engage and pass the “message” to outer layer (a view, a sound subsystem, a social platform, a REST api, whatever)..
And vice versa. If end-user needs to interact with an app, then glue passes message from view (clicks) down to domain via input methods.
See it in action: a glue for web DOM representation of a domain. (we do have different glue for playing sounds, or talking to Facebook social platform)
And to have complete picture of what we call view layer, let’s see view:
How persistence is handled? Might be this way:
Now, these are the basics! Knowing that let’s move on further!
We do start with a browser. We code in the browser. We separate code into clean layers. View knows nothing about domain and domain knows nothing about MANY services that use it.
Glue (many glues) ties together these layers.
Domain tests can be written in command line. Domain is written as the first one. With it being clean we achieve simplicity and less bugs if any. As it is often written in same words a human would describe a business process in more or less “programming English”.
Having it AND NOT WORRYING about persistence, not worrying about backend!, we can provide early, working in the browser prototype of the software. This can be shown to end user and validated and again modified, and… again.
This way of working has been verified and used through several of our apps. We have games too. Games that players play and have fun. And developers have fun too.
There are at least 2 realities. And any way, backend is just a plugin to our overall architecture. Can be plugged in, or off.
- we care about hacking via browser
- we don’t care about hacking
Sometimes it’s just the surroundings of an app business, that we may allow mild hacking. If minor things happen, we just save developers time and $$$ and move on to the next project.
Or maybe it’s a safe intranet environment, where we can trust users? Or we can just trace their actions via logs. If that’s OK that’s great!
Sometimes though, we would be sorry if only frontend would drive backend persistence. And thus for example a HTML5 game player could influence his earned points, and made himself the one in top of hiscores. We’ve seen gamers with 999999999 points, haven’t we?
There are really 2 ways we can take:
We know it’s caveats but that might be the only option. Not so bad, if done AFTER frontend is complete. As it’s just a rewrite + maintenance after (keeping code in sync)
- we reuse domain JS code in some backend that can run native JS. Node obviously. What else? JVM? Ruby? Tell me please.
It’s not so easy for non-Node platforms, but can be done. And if the domain logic is heavy, then it just can make sense.
A word of advice. A browser is an ideal platform to do real Model-View-XXX strategy. It’s where your domain code will shine again. Stress free, elegant, and most important .. nothing new really.
You can always follow me on @nthx17. Further working ideas from space will come soon :-)