Show HN: The Obvious Architecture
obvious.retromocha.com
obvious.retromocha.com
If I understand correctly, the only thing this is doing is converting a compile time dependency into a run time dependency. But the dependency is there nevertheless in the names of the fields, types, etc.
There is no explicit dependency between layers. Dependency injection is used to pass the data jacks into the actions. That allows for incredibly fast tests and pluggable data persistence.
I think programminggeek nailed it here. If you watch Bob's talk, then watch the Obvious screencast, you'll see that this is a serious attempt at this dream architecture.
http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...
* Backend server A provides a REST API and stores data to a NoSQL database.
* Website B communicates with A or can work standalone with MySQL.
* Android client C communicates with A, and also caches data to its own SQLite database for off-line access.
A is written in Scala, B in Python and C is native Android.
How would you use Obvious in this scenario? Is it one big Obvious architecture or three separate Obvious instances? The REST API here is both a delivery and a plug, no?
That being said, in each instance you could use the Obvious approach to structure each app. If you were using the same language in each place, you could theoretically reuse the "app" structure in multiple places, so that your same business rules and validations would exist in each instance.
For the structure you describe, if you were willing to do Website B in something that worked with the JVM, you could write your "app" in Java or Scala and package it as a JAR file. So, you could use Jython to run your python code on the JVM. Then, you could reuse your app library in each project.
Assuming your app is a JAR file, your structure would look like this:
REST API - Scala for delivery -> App JAR -> NoSQL plug
Website - Java, Scala, Jruby, or Jython for delivery? -> App JAR -> API plug or MySQL plug
Android - Java for delivery -> App JAR -> SQLite db plug & API plug
If you were able to run the whole thing on the JVM you could place them all in one project, or separate projects, it's really up to you. And yes, the REST API would act as a delivery mechanism for system A and a plug for systems B and C.
Obvious seems like a very good fit for all the B's (closely related Python websites).
But I don't know about Scala backend though: do you have thoughts on packaging of different deliveries for static languages in Obvious? Because if there is only one folder structure, you typically (with Maven) get only one JAR.
To get many JARs the folder structure quickly turns into:
app/actions/common/src/main/scala
app/actions/featureset1/src/main/scala
app/actions/featureset2/src/main/scala
delivery/plug1/src/main/scala
delivery/plug2/src/main/scala
etc.
which isn't very pretty nor obvious (sic!). The other option I can think of is to have one big JAR that always has every plug and delivery baked in with a folder structure like:
src/main/scala/[Obvious folders]
and plugs and deliveries would then need to be activated runtime with configuration files. But that also seems sub-optimal: I don't want to have any code in production I'm not going to use.
Am I missing something?
I think you could have each JAR have src/main/scala/[Obvious folders] type structure, so for your app.jar you could have src/main/scala/app and src/main/scala/external for the external.jar.
You can make it as granular as you need, but the more you break things up, the more packaging overhead you'll incur.
That makes you want to _not_ have one JAR and then keep your fingers crossed that it is configured right. Optimally things should just work (tm) in production without any configuration. (Not to mention if you try to sell your software, you can not sell just one package because just by changing the configuration from delivery1 to delivery2 you get the second product for free.)
With dynamic languages what you presumably would want to do is just zip the right files and that's that (although you still need system tests that verify that the zip has the right stuff in it), but with static languages it's more tricky. One way to get that would be to have A-core.jar that has all code that is common to every installation, and then A-installation1.jar with the right delivery and plugs for the first installation, and A-installation2.jar for files for the second installation etc.. Because in the installation JARs there would be only one delivery, and only one plug per jack (and one jack per contract), things would work without any additional configuration.
Now the problem with Maven is that you can't easily get many JARs from one src/main/scala. Or can but things get complicated when start releasing stuff to repositories. Which results in the really messy directory structure I outlined above, or then you would have to figure out some other directory structure that would work better. Of course the best would be to hack Maven to work around the "one JAR per src/main/scala" limitation. Or write a maven-obvious-plugin. :)
The backstory here is that packaging tools easily mandate directory structure. Directory structure in turn directs source packages (inverted URL in Java), and source packages tend to influence the architecture.
The cool thing in Obvious is that by making the directory structure itself reflect the architecture, code is automatically steered towards good practices. Of course you can create a great code architecture with any directory structure, but in many cases _where_ you can put stuff, influence _what_ you put in there.
So in some cases, it's actually the supposedly trivial packaging of sources that is a culprit for bad decisions. Which IMO makes packaging also non-trivial from an architecture standpoint.
The key difference in Obvious is that it tries to be more purposeful in the naming and the structure of the parts of your application. The "app" describes your business logic and entities, the "external" defines external interfaces to data sources, and the "delivery" defines the delivery mechanism like a web app, command line, rich client, or whatever.
To drill down deeper into things being given obvious names and structure, the "app" is structured with single purpose use case objects called actions. Core data structures(business objects) are defined by entity objects. In that structure you can look at the app folder and see what the system data is and what the system does. The idea is that the system's intent and functionality would scream at you just by looking at it from a high level.
Uncle Bob's keynote at Ruby Midwest 2011 articulates it a bit better than maybe I do - http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...
It can run as a rails app, sinatra web app, sinatra api app, command line app, or desktop app using jruby. Backend database is pluggable and can use json files, mongo, mysql, or even its own api as a data source without switching the application code itself.