The most straightforward one: just make your protocols a thing that your storage medium implements, instead of your records. E.g.:
(defprotocol ThingStore
(load-item [store item-id])
(load-items [store item-ids])
(find-item [store item-name])
(save-item [store item]))
(defrecord DatabaseStore
ThingStore
(load-item [store item-id] ...))
Otherwise, Clojure does have multimethods, but the dispatch mechanism doesn't work the same was as Common Lisp's. You could probably combine multimethods with a marker protocol to do what you're trying.
;; First, an empty marker protocol
(defprotocol Loadable)
;; And something that you want to load
(defrecord Thing [data]
Loadable ;; mark that it implements that empty protocol
)
;; Define a multimethod dispatch function that will figure out the right implementation to call
(defn loadable-dispatch
[item]
(cond (satisfies? Loadable item) ::load-one-item
(and (sequential? item) (every? #(satisfies? Loadable %) item)) ::load-many-items
:else nil))
;; The multimethod itself
(defmulti load-item loadable-dispatch)
;; And implementations
(defmethod load-item ::load-one-item
[item]
"Load one item.")
(defmethod load-item ::load-many-items
[items]
"Load many items.")
user> (load-item (map->Thing {}))
"Load one item."
user> (load-item [(map->Thing {}) (map->Thing {})])
"Load many items."
Overall, you're probably better off with the first option, but you can make multimethods work for you here if you want.