Attribute naming conventions


#1

There doesn’t seem to be much available online regarding datomic attribute naming conventions. I started thinking about this because I realized I was using - and . inconsistently in my attribute names. I’m hoping to find out what people think is the best convention.

I find that in real-world databases you end up with more nested names than those found in the example schemas. For example, in an application for making surveys, we have this attribute: :question.contact-info.channel/validation. “Contact Info” is a type of question that allows specifying different contact channels (like email, phone, etc) and input validation for each channel. So this naming convention is basically class.sub-class.inner-class/attribute. I’m using “subclass” and “inner class” to mean roughly what they do in java. This convention is imperfect because the separator for subclasses is the same as the one for inner classes. The ambiguity can be removed by introducing a different separator character: class#sub-class.inner-class/attribute.

To more thoroughly specify this convention:

  • - is a word separator, like in clojure var names.
  • . indicates nesting/“inner classes”.
  • # indicates subtypes.

I haven’t actually started using this convention yet, but it seems like an improvement over the ad hoc approach I’ve been using. Thoughts?


#2

Hi Tom
I’ve been thinking a lot about this recently. I think about this very differently than you so maybe you will find it interesting.

My current reasoning is:

  • meaning is given by names, not namespaces
  • namespaces are for solving name collisions
  • namespaces are not for classifying things into hierarchies

So is :question.contact-info.channel/validation really any more meaningful than :surveymonkey.survey/contact-channel-validation1? (Or something)

The clojure/core namespace is a great example. We have clojure.core/assoc, clojure.core/cond, clojure.core/defonce, etc. If we were to classify these things, they would be at very different places in the taxonomy tree. But as a shared vocabulary, they don’t collide and we just refer to them as “clojure/core” and it all works out.

Datomic.api is another example. d/q, d/transact, d/datoms, d/remove-tx-report-queue share a namespace.

Much more important I think is a name’s provenance – it’s origin, which project made it first, which team maintains it. But its okay to use an attribute in many projects or many contexts, without aliasing it into some new naming convention.

Even if we limit to one or two segment depth (say :organization.provenance for large multi-org projects that process data from many APIs) there is an infinite space of namespace names to choose from.


#3

I haven’t thought about datomic attributes in this way because I’ve never needed to combine my db with another one; naming collisions haven’t been a problem for me yet. If we assume your three bullet points, the part where a convention is helpful moves to the right of the /. Instead of :question#contact-info.channel/validation, the attribute could be something like :my.org/question#contact-info.channel.validation. In terms of conveying information, that’s better because now the attribute tells us where it comes from, but in terms of ergonomics it’s worse because the name is huge.


#4

Well for example :db.cardinality/one could flatten to just :db/one, without (in my opinion) confusing anyone. Eliding the hierarchy is not going to cause anybody to accidentally write {:db/unique :db/one}. And you can still validate it with a spec.


#5

Shortening :db.cardinality/one to :db/one might not confuse people, but it does leave more room for future naming conflicts and it does convey less information. My goals with defining a naming convention are to prevent naming conflicts, have an obvious route from concept to keyword, and make it obvious later what existing names mean and where they should be used. I’m less concerned with where the convention lives (in the namespace, name, or both) and more concerned with achieving those goals.

How do you use attribute namespaces in your datomic applications? For hyperfiddle are they all just :hyperfiddle/whatever-attribute? Do you put any heirarchy information into the attribute name? If not, how do you avoid conflicts and how do you make sure the meaning of a name is obvious?


#6

We always write docstrings which helps document the intended semantics of shortnames of the form :db/one.

I have no good answer for picking good names.

Our core schema is pretty small but we kind of screwed this up. We have a lot of :hyperfiddle.x.y.z/foo and it is hard to remember. Also things get refactored and then namespaces stop making sense. I didn’t know about :db/ident aliases until recently so that will help.

In the future I’d like to move towards

  • :hyperfiddle/ core things
  • :hfnet/ cloud only services
  • :hfinc/ business data

Like why is :hfnet.user/primary-email better than :hfnet/primary-email? If it has a primary-email it’s gotta be an upsert to a user, right? No clear answer here

A pro of the tradeoff is we often use the same attribute in many “classes”, e.g.

  • :hyperfiddle/owner
  • :hyperfiddle/markdown

We specialize things like :hyperfiddle/fiddle-ident (:db.unique/identity) (over just :db/ident) because it takes a role similar to “entity types”.

This is all just my opinion and definitely out of sync with examples I’ve seen from Cognitect.


#7

I would try to be careful to not map too directly a Datomic schema with the idea of class (type). One of the interesting aspect of the Datomic information model is that it allows to think in terms of composable sets of attributes rather than types. An entity is not restricted to one type but can have attributes from different sets. One example is the Codeq schema: https://github.com/downloads/Datomic/codeq/codeq.pdf See how the namespaces group a set of related attributes together and how entities mix those attributes from different namespaces?