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.