> On 2016-08-27, at 09:09, Georgi Danov <georgi.da...@gmail.com> wrote:
> 
> I have om.next app and am building two different sets of mutate/read fns 
> because I want to have switchable persistence.
> 
> I am pondering over the design of the coupling between the UI and the data 
> layer and in particular — how to ensure that I spot as early and reliable as 
> possible inconsistencies between the persistence implementations and the UI.

If I understand you correctly, you should definitely keep your persistence 
implementation and the UI separate. Indeed, the client shouldn't care at all 
what the persistence is on the remote (server) side.

Here's how I've structured my application:

* Om Next client (using Untangled client) calls the remote API with read and
  mutate calls.

* Web server (e.g., Pedestal, Ring, HTTP Kit) which includes the Om parser
  (ns myapp.server.api.parser)
* The Om parser read and mutate implementations are pretty simple.
  They primarily call the core business application code which is in its
  own namespace (myapp.server.application) and knows nothing about the
  web server. Likewise there's no business logic in the parser. It pretty
  much takes data from the request and session, passes them to the application
  functions, and wraps responses from the application in the
  appropriate {:action (fn []...)} and {:value {:tempids {}}} maps.
* The application code knows nothing about the persistence layer implementation.
  I have protocols set up that define the interface between the two,
  and different persistence types implement the protocol functions. In my
  case I have Postgres (production) and H2 (test) implementations. Granted,
  they're both JDBC, but they're different enough that I've found it useful
  to use both. The functions that need different implementations for H2 and
  Postgres I have implemented as multi methods that switch on the database
  server type.

So, on the server I have

  (ns myapp.server.component.api.parser
    (:requires [com.stuartsierra.component :as component]
               [myapp.server.component.application :as app]
               [om.next.server :as om))

  (ns myapp.server.component.application
    "Business logic of the application"
    (:requires [com.stuartsierra.component :as component]
               [myapp.server.component.application.protocols :as p]))

  (ns myapp.server.database.sql
    "Persistence layer."
    (:requires [com.stuartsierra.component :as component]
               [clojure.java.jdbc :as jdbc]
               [myapp.server.component.application.protocols :as p]
               [myapp.server.component.database.impl.sql :as sql))
    
Notice in this set up, the persistence layer knows nothing about the parser.
Indeed it knows nothing about even the application other than the protocols
its implementing. For simple reads and mutates, the functions at each layer
look very similar, and there's a lot of stuff that's passed through, but I've
found it helps me think about the application by keeping everything decoupled.

Because spec.fdef doesn't work with protocol function implementations, I have
the implementation functions in turn call plain functions. I think this might
actually be inverted from the way it should be, because I should have the spec
in the application code rather than the implementation code to reduce code 
duplication, but that's refactoring for another day.

> Example: changing the data structure by adding a field, putting this in the 
> UI but forgetting to adapt one of the read functions. I've considered using 
> clojure.spec in a thin repository layer that only validates the data coming 
> from the persistence implementation, however the general message I've heard 
> is that clojure.spec is not supposed to be used in production mode.

I've just started using clojure.spec, and haven't done any profiling to see 
what the impact is. So far I've found fdef useful in catching malformed 
arguments. I include the spec definitions in the source, but only instrument 
them in testing.

> What's your advice on structuring the mutation functions? Is it advisable to 
> use om/transact! directly in the UI code, or do you have some API (like 
> Repository pattern) that hides the details?

On the client side I have a mix of using om/transact in the UI code in the UI 
and wrapping the om/transact calls in functions. I'm also trying to figure out 
how to make reusable components. My ui.cljs file is getting longer than I'd 
like, so I'm going to be looking at how to split that up in logical ways.

I'm not familiar with the Repository Pattern by name, but I definitely want to 
hide implementation details (both on the web server and database sides) from 
application logic.

I'm wondering if I've completely misunderstood your questions. In particular, 
when I think persistence I think server side. Perhaps you're thinking of 
storage on the client? Given that this is on the ClojureScript list maybe I'm 
off-base discussing so much of the server-side.

Hope this helps.

Michael Glaesemann
grzm seespotcode net



-- 
Note that posts from new members are moderated - please be patient with your 
first post.
--- 
You received this message because you are subscribed to the Google Groups 
"ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojurescript+unsubscr...@googlegroups.com.
To post to this group, send email to clojurescript@googlegroups.com.
Visit this group at https://groups.google.com/group/clojurescript.

Reply via email to