
Show HN: The Obvious Architecture - programminggeek
http://obvious.retromocha.com/#intro
======
huherto
> The fundamental data structure of data transport between layers is a hash.
> This is for both incoming and outgoing data from the each layer. There are
> many benefits to this approach. The biggest benefit is that each layer stays
> decoupled from the other layers and that hashes act as messages passed
> between the layers.

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.

~~~
programminggeek
Well for now Obvious is implemented in ruby, so everything happens at runtime.

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.

------
bryanthompson
After I saw @unclebobmartin speak at Ruby Midwest last year, I got _so_
flipping excited. I walked away with goals of having tests that ran instantly,
layers that were as independent as possible, and wanted to build this master
framework he described so cryptically.

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-...](http://www.confreaks.com/videos/759-rubymidwest2011-keynote-
architecture-the-lost-years)

------
ttiurani
Really cool! However I'm struggling a bit to see how to use the architecture
in larger projects. I have a (grandiose) idea for a group of web sites with
the following structure:

* 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?

~~~
programminggeek
Wow, that's a crazy structure. I like it. Conceptually, Obvious is language-
independent, but for now it's only implemented in Ruby. It would be pretty
easy to implement elsewhere in Java, Scala, Python, or whatever.

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.

~~~
ttiurani
The motivation for the two server backend is that I currently have one working
standalone B and will build a second B soon to serve a closely related
purpose, and really don't want to duplicate the work or the data. That's why a
backend server that stores common data and also most of the heavy business
logic looks like the best solution at the moment.

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?

~~~
programminggeek
Can't you put the whole app folder into one JAR? Also, you could put all the
jacks and plugs into a a separate JAR. So, you have two JAR files to include
into each project.

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.

~~~
ttiurani
The problem with one JAR is that you will then have to configure runtime in
production which delivery is used and also, if there are alternative plugs (or
jacks), what set of plugs you activate. That would have to be done with an
external config file or database row, command line parameters or some super
GUI. The problem is that those are really dangerous configuration options,
because changing any of them, results in catastrophic failure: e.g. someone
might accidentally change a plug configuration option to another value, and
your MySQL turns into MongoDB which you don't have.

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.

------
przemekpokrywka
How does Obvious relate to Hexagonal Architecture (AKA Ports-and-Adapters,
<http://alistair.cockburn.us/Hexagonal+architecture>)? I'd like to suggest
that it's not bad, but others have found the right answer already. Or does
Hexagonal leave some important problems unsolved?

~~~
programminggeek
Obvious is a clean architecture more directly in the style of Uncle Bob's
Screaming Architecture. It's actually a kind of hybrid between Screaming
Architecture and Ports-and-Adapters. In fact, I looked at Hexagonal
architecture a lot and purposely avoided the terms ports and adapters in place
of jacks and plugs even though they are conceptually the same idea.

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-...](http://www.confreaks.com/videos/759-rubymidwest2011-keynote-
architecture-the-lost-years)

------
programminggeek
I'm one of the creators of Obvious. If you have any questions, fire away!

------
joyinsky
Does any functional example of this architecture exists?

~~~
programminggeek
Yep, there is an example twitter clone called Status.
<https://github.com/RetroMocha/obvious_status>

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.

