Search code examples
schemaforeign-key-relationshipdatomicenforcement

Datomic - db/isComponent equivalent to enforcing a foreign key dependency?


In the Datomic Schema doco - they mention a schema attribute called db/isComponent. This appears to refer to relationships defined by :db.type/ref.

The db/isComponent isn't used in the Seattle example. Is it fair to say that :db.type/ref relationships in Datomic are not 'enforced' (to use relational database foreign key dependency concepts) - unless you set them with db/isComponent?


Solution

  • Containment Relationships (:db.type/ref + :db/isComponent)

    :db/isComponent is used to specify containment relationships i.e. the composition relation from UML. You can think of it as a "A has B" kind of relation. Modeling part of a simple blog is a clear example:

    Article-Comments

    In Datomic, if you use the :db/isComponent attribute as part of Article-Comments relationship above, retracting an article will retract all its comments too. For a full code sample, look at the Datomic: containment relationships i.e. db/isComponent gist.

    Note there's nothing in Datomic that prevents you from adding the wrong kind of entity to a :db.type/ref attribute. In the sample above, Datomic would let you add a reference to an "Author" entity (instead of a Comment) without really caring. That's where foreign key constraints come into play.

    Foreign key constraints (:db.type/ref + database functions)

    Datomic defines relationships using the :db.type/ref attribute but doesn't really enforce anything about them. To use arbitrary foreign key constraints, you need to use database functions instead.

    In the Seattle database you mention, :community/orgtype attributes are supposed to reference only a few allowed enum values (:community.orgtype/*) but there's actually no enforcement at runtime:

    enter image description here

    To show how arbitrary foreign key constraints can be implemented in Datomic, I wrote a database function (called add-fk) that prevents the wrong enum value from ever be associated with :community/orgtype attributes.

    For a full code sample, look at the Datomic: database functions and foreign-key constraints gist. For example, the add-fk database function behavior is shown below:

     ;; will succeed
     [[:db/add #db/id [:db.part/user] :community/name "15th Ave Community"]
      [:add-fk #db/id [:db.part/user] :community/orgtype :community.orgtype/personal]])
    
     ;; will fail
     [[:db/add #db/id [:db.part/user] :community/name "15th Ave Community"]
      [:add-fk #db/id [:db.part/user] :community/orgtype :community.type/email-list]])
     ;; java.lang.Exception: :community.type/email-list is not one of 
     ;; [[:community.orgtype/community], [:community.orgtype/commercial], 
     ;; [:community.orgtype/personal], [:community.orgtype/nonprofit]]