Design Patterns#
OCF Types vs OCF Objects#
You may notice this distinction in our repo organization, yet both OCF Types and OCF Objects are
commonly JSONSchema object
types (basically meaning they are JSONs).
-
An OCF Object is a schema with a JSONSchema types of
object
that MUST have a unique Id field in each instance of its schema. -
An OCF Type is meant to describe a specific data type that is does not have a unique ID and will be nested somewhere within an OCF Object. These usually have a JSONSchema type of
object
, but they can also be JSONSchema primitives like strings, numbers, etc. that have specific validation rules (e.g. a regex pattern). They do not have unique IDs.
OCF Schemas Rely Heavily on Object Composition Patterns#
In order to improve code quality, reduce repetition and provide for a better developer experience,
OCF schemas rely heavily on object composition.
In the
primitives folder,
you'll see a folder structure that mirrors the overall /schema
folder. Where a number of related
OCF Objects or OCF Types share properties, we create a "primitive" object in this primitives folder.
This primitive's path in the primitives folder must mirror the path to the OCF Object(s) or OCF
Type(s) composed from it. We incorporate the properties in these primitives into OCF objects by
using the JSONSchema
allOf property.
Where there are different "flavors" of a given primitive - e.g. the primitive ConversionRight
is
composed into OCF Types of ConvertibleConversionRight
, a StockClassConversionRight
or a
WarrantConversionRight
- the final OCF Types / Objects must have a type
that holds the specific
flavors type enumeration - e.g. STOCK_CLASS_CONVERSION_RIGHT
for StockClassConversionRight
. This
avoids validation problems where two flavors must have identical properties but different allowable
property values.
Here are a couple of concrete object composition examples:
- The
ConversionTrigger
OCF Types likeAutomaticConversionOnConditionTrigger
andElectiveConversionOnConditionTrigger
are all composed fromConversionTrigger
. The primitive can be found at /schema/primitives/types/conversion_triggers/ while the OCF Types composed from this primitive can be found in /schema/types/conversion_triggers/. - All OCF Objects must be composed of the
Object which ensures there is a
required object id field on all OCF Objects. This can be found in
/schema/primitives/objects
as all objects in the/schema/objects
folder will incorporate its properties. - All of our transaction events must have properties listed in the Transaction schema. Then, different groups of transaction events share some common properties, and these are enforced by incorporating more specific primitives - e.g. all issues (whether of stock, plan securities, warrants or convertibles) must incorporate the properties set forth in Acceptance in addition to the properties in Transaction.
What's with the empty properties (e.g. {}) in your schemas?#
You'll notice that required OCF type and object properties that are incorporated via
object composition have empty objects as their schema values in
the OCF object schemas (i.e. "id": {}
). This is due to how JSONSchema validators interact with the
"required" property. If validators don't find a required property in an object schema, even if it's
one of the primitives the object is composed of, most (all?) JSONSchema validators will throw an
error. As a result, we need to add required, "inherited" properties to the final OCF object schemas.
They don't actually need to be redefined, however, so we just assign these repeat properties a value
of {} in the schema as JSONSchema validators can import the property details via allOf. Our
documentation generator looks back to the full details of the property from the inherited schemas,
however, and the documentation shows the full property details inherited from the primitives. Unless
you're developing OCF schemas, these implementation details probably won't matter to you, and you
can rely on our documentation for definitive documentation of the necessary properties and all
details thereof.
Don't Add Additional Properties to OCF#
You may notice that the primitive schemas do not prevent the inclusion of additional properties,
whereas all OCF Types and OCF Objects that are also JSONSchema objects
(basically anything that's
not just an enum or primitive data type with validation check) have additionalProperties
: false
to prevent the inclusion of additional properties. This is due to primitives not being meant for use
by consumers of the OCF standard. As we build more complex objects from primitives, we need to allow
the primitive to have the additional properties required by the final OCF Objects and Types or else
you'd get validation errors. We do not want the OCF Types and OCF Objects that are meant for use by
the community to have additional properties, however, as we want to avoid situations where
third-parties implementing OCF add custom or undocumented fields and types. This could cause
unanticipated compatibility issues and collisions with future versions of OCF, if, for example, a
popular implementation of OCF used a custom property with a name that we later want to add to to the
official standard.