Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by
retagged by

I have a scenario where I'd like to define some attributes that are internal and common, and some other attributes that are external and dynamic, both related to the same "things". Specifically, I am trying to model a trading library. There are many different exchanges/brokers/services, but all of them talk about similar/equivalent things.
Let's take the example of an order, you can have some attributes that can be common in all services (they don't necessarily appear on all services):

(spec/def :order/id string?)
(spec/def :order/quantity number?)
(spec/def :order/side #{:buy :sell})

Then, every external service can have specific attributes related to how that service handles orders:

(spec/def :service1-order/execution-instructions (spec/coll-of string?))
(spec/def :service2-order/iceberg-quantity (spec/coll-of string?))

I was thinking on having a multi-spec returning schemas, so each service can define what schema to use for an order, on a key like :service/name, but all of them can be "selected" from :order/order.

(defmulti order-schema :service/name)

(defmethod order-schema :service1
  (spec/schema [:order/id :order/quantity :order/side :service1-order/execution-instructions]))

(defmethod order-schema :service2
  (spec/schema [:order/id :order/quantity :order/side :service2-order/iceberg-quantity]))

(defmethod order-schema :default
  (spec/schema [:order/id :order/quantity :order/side]))

(spec/def :order/order (spec/multi-spec order-schema :service/name))

This can be useful also when nesting objects. Imagine I have an account that will contain orders. The account can also be specced as a multi-spec for each service:

(spec/def :account/id string?)
(spec/def :account/orders (spec/coll-of :order/order))

(spec/def :service1-account/type #{:spot :margin :futures})

(defmulti account-schema :service/id)

(defmethod account-schema :service1
  (spec/schema [:account/id :account/orders :service1-account/type]))

(defmethod account-schema :default
  (spec/schema [:account/id :account/orders]))

(spec/def :account/account (spec/multi-spec account-schema :service/id))

This would allow to select in the following manner:

(spec/def ::service1-account-info (spec/select :account/account [:service1-account/type :account/orders {:account/orders [:order/id :order/quantity :service1-order/execution-instructions]}]))

This way, external services can specify the information they handle about the common layer domain attributes and entities, as well as specify their specific attributes. The user is able to reuse a set of common functions that operate on the common attributes, and if different services provide these common attributes, they can be used interchangeably, while keeping a way to create functions and selections that are specific to specific services.

Does this use case make sense? Is there a different way to model this that would be better?


1 Answer

0 votes