> 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.