Where to place database access / functionality in clojure app?

I am writing a small Clojure application that has a lot of interaction with the MongoDB database with 2-3 different collections.

I come from the OOP / Ruby / ActiveRecord background, where the standard practice is to create one class for each data model and give everyone access to the database. I started doing the same in my Clojure project. I have one namespace per “data model”, and each of them has its own database connection and CRUD functions. However, it is not very functional or clojure -like, and I was wondering if there is a more idiomatic way to do this, for example, to have a data or database namespace with functions such as get-post , and restrict access to the database only for this space names.

It seems like it would be useful to isolate the database client dependency from a single namespace, and also to separate pure functions from those that have side effects.

On the other hand, I would have another namespace that I would need to reference from many different parts of my application, and have a namespace called “data” just seems strange to me.

Is there a regular, idiomatic way to do this in Clojure?

+6
source share
2 answers

A good and possibly the most idiomatic one ( scoring “accept” on the Clojure radar ) to manage the state of the Clojure app is an offer proposed by Stuart Sierra great Component . In short, the Component’s philosophy is to store all resources with state in a single system card that explicitly defines their mutual relations, and then archive your code so that your functions simply pass state to each other.

+2
source

Connection / access to the environment

One part of your system will control the “machine” of your application: start a web server, connect to data warehouses, get configuration, etc. Put this part in the namespace namespace from your business logic (your company logical namespace should not know about this namespace!). As @superkondukr said, Component is a proven and well-documented way to do this.

The recommended way to communicate with the database (and other environmental dependencies) for your business logic is through function arguments, not global Vars. This will make everything more verifiable, REPL-friendly, and explicit who depends on whom.

This way your business logic functions will receive the connection as an argument and pass it on to other functions. But where does this come from in the first place? The way I do this is to attach it to events / requests when they log in. For example, when you start your HTTP server, you attach a connection to each incoming HTTP request.

Namespace Organization:

In OO, the usual data support is instances of classes that represent database objects; to provide an OI idiomatic interface, business logic is then defined as the methods of these classes. As Eric Normand said in a recent newsletter, you define your model “names” as classes and “verbs” as methods.

Because Clojure focuses on simple data structures for conveying information, you really don't have these incentives. You can still organize your namespaces with an entity to mimic this, but I really don't think it's optimal. You should also consider the fact that Clojure namespaces, unlike classes in most OO languages, do not allow circular references.

My strategy: organize your namespaces using a case .

For example, imagine that your domain model has users and messages. You may have the myapp.user namespace for CRUD users and core business logic; Similarly, you can have the myapp.post namespace. Perhaps in your application, users may like the messages, in which case you will manage this in the myapp.like namespace, which requires both myapp.user and myapp.posts . Perhaps your users may be friends in your application that you will manage in the myapp.friendship namespace. Perhaps you have a small backoffice application with data visualization about all of this: you can put this in the myapp.aggregations namespace, for example.

+2
source

All Articles