Spec-ing entities and how to organize the specs and our entity keys? A new approach?

Where I’ve landed with my experimentation is something very similar to how symbols and namespaces work, except for keywords.

I have a defalias function which works the same as alias except under the hood it’ll call create-ns to allow the alias to be created in the current code namespace:

(defalias 'foo 'com.bar.baz.foo)
;; => ::foo is now resolved to :com.bar.baz.foo in the current namespace

But I also created in-scope which works like in-ns except it creates an “alias scope”. So I can do:

(in-scope 'com.bar.baz)
(defalias 'foo)
;; => ::foo is now resolved to :com.bar.baz.foo in the current namespace
(in-scope 'com.fizz.buzz)
(defalias 'foo)
;; => ::foo is now resolved to :com.fizz.buzz.foo in the current namespace

So what I do now is I can have something like this:

(ns com.org.app.data-model ...)

(in-scope 'com.org.app)

(defalias 'user) ;; Means ::user will be an alias for com.org.app.user because that's the current scope

(s/def ::user/name string?) ;; ::user/name resolves to :com.org.app.user/name
(s/def ::user/email string?)
(s/def ::user/user
  (s/keys :req [::user/name ::user/email]))

And in another file:

(ns com.org.app.core
  (:require [com.org.app.data-model]))

;; Can use in-scope here as well for the default alias scope so you don't have
;; to repeat the full namespace in all defalias calls here.
(in-scope 'com.org.app)
(defalias 'user)
(defalias 'cart)
(defalias 'transaction)

;; If you have an entity name conflict, you can use the 2-ary variant
;; to alias it directly
(defalias 'other-user 'com.org.other-app.user)

(defn user->other-user [user] ...)
(s/fdef user->other-user
  :args (s/cat :user [::user/user])
  :ret ::other-user/user)