YAML, known for its simplicity and human readability, is a widely used data serialisation language often employed for configuration files. Given this function, the question arose: What if it could be used to define bespoke business domains and logic, giving end users the ability to describe domain entities and their relations in a way that’s almost like spreadsheet functionality?
In this article, we explore the idea of extending YAML beyond its conventional use as a static data representation tool. We do this through the lens of a a work-in-progress project that aims to give users the ability to define custom domain entities and their logic through YAML files.
Introducing Functional Domain Entities
The following example illustrates a YAML-based approach to defining custom functional domain entities and their relationships using formulas. Let’s break it down to understand its purpose and what users can achieve with it.
# The Product template
Product: !template
code: !input [text, '']
price: !input [number, 0]
quantity: !input [number, 0]
total: !formula this.price * this.quantity
totalWithTax: !formula this.total * 1.15
# The Invoice template
Invoice: !template
items: !input [list, Product]
total: !formula SUM(this.items.total)
totalWithTax: !formula SUM(this.items.totalWithTax)
# Example Invoice instance
invoiceA:
extends: Invoice
items:
-
extends: Product
code: 'A'
price: 10
quantity: 2
-
extends: Product
code: 'B'
price: 20
quantity: 3
-
extends: Product
code: 'C'
price: 30
quantity: 4
Defining Entities with YAML
At the core of this project is the ability to define custom functional entities. Each entity is defined in YAML and can be extended from other template entities, much like inheritance in object-oriented programming. This allows for a hierarchical structure of entities where child entities inherit properties and behaviours from parent entities. In the example mentioned above, Product and Invoice are entity templates that can be extended or implemented, as demonstrated in the example invoice entity.
The YAML tags used in this project provide bespoke semantics to define the structure and behaviour of functional domain entities. Let’s delve into what each tag signifies:
- !template: This tag indicates that the YAML block defines a template for a functional entity. In the example, both Product and Invoice are templates. Templates serve as blueprints for creating instances of entities.
- !input: This tag specifies an input attribute for an entity, defining its data type and default value. For instance, the Product template has attributes like code, price, and quantity defined as inputs.
- !formula: This tag introduces a formula attribute, allowing users to express dynamic relationships between attributes. In the example, the total attribute of the Product template is calculated as the product of price and quantity, while totalWithTax is derived by applying a 15% tax to the total.
Hierarchical Structure
The use of the “extends” keyword creates a hierarchical structure among entities. In the invoice entity example, it extends the Invoice template, inheriting its structure and behaviours. Similarly, each item in the invoice extends the Product template, forming a hierarchical relationship.
This hierarchical structure enhances modularity and reusability. Changes made to a template propagate to all instances and extensions, promoting consistency and reducing redundancy. It mimics the principles of object-oriented programming, providing a familiar paradigm for users.
Expressing Logic with Formulas
A notable feature of this project is the ability for users to express custom logic through formulas. Like spreadsheet functions, these formulas offer a means to calculate values based on the data housed within the YAML file. They’re flexible enough to pull in values from other properties within the same entity or even from different entities.
For instance, let’s take a look at the Product template in the previous examples. The total attribute is defined as follows:
total: !formula this.price * this.quantity
This formula dynamically computes the total by multiplying the price and quantity. It’s a practical illustration of how you can integrate sophisticated business logic directly into your YAML definitions.
Here is an example of how the domain can now be functionally interrogated:
console.log("Invoice total: ", context.evaluate("invoiceA.total"));
>> Invoice total: 200
console.log("Invoice total with tax: ", context.evaluate("invoiceA.totalWithTax"));
>> Invoice total with tax: 230
Use Cases and Flexibility
The possibilities that this extension of YAML presents are vast and can be applied to a wide range of use cases. Let’s explore a few scenarios where this approach could be immensely beneficial:
- Educational Institutions In an educational context, this approach could be used to define grading and assessment criteria. Educators and administrators could create functional entities for courses, assessments, and students, along with complex grading rules, and have them all defined in a human-readable YAML format.
- Financial Modeling Financial analysts could use this approach to define complex financial models, including formulas for profit and loss calculations, investment portfolios, and risk assessment.
- Business Rules and Workflows In a business environment, functional entities could represent various aspects of a company’s processes. The ability to define custom logic through formulas can be a powerful tool for modelling business rules and workflows.
- Game Design Game developers could define entities for game characters, items, and abilities, and use formulas to determine character statistics and interactions.
Prototype Implementation Notes
During our prototype development, two key libraries played a pivotal role: Chevrotain for parsing and interpreting formulas, and js-yaml for YAML parsing.
Chevrotain, a parser-building toolkit for JavaScript, handles the parsing and interpretation of formulas, converting them into expression trees.
js-yaml is used for YAML parsing, allowing some useful extensions through custom tags.
Challenges and Considerations
While the concept of using YAML to define custom domain entities and logic is exciting, it comes with certain challenges and considerations:
- Complexity: As the domain logic becomes more intricate, YAML files may become complex and challenging to manage.
- Validation: Ensuring that formulas and references are used correctly and do not lead to circular dependencies or undefined behaviour is crucial.
- Documentation: Proper documentation and validation tools would be essential to guide users in creating meaningful and functional YAML structures.
- Security: Allowing custom logic in YAML files can open up potential security risks, especially when software processes these files.
Conclusion
The extension of YAML to define functional domain entities and logic through formulas is a fascinating exploration of YAML’s potential beyond simple data representation. This project empowers users to create bespoke business domains and relationships in a format that is both human-readable and highly customisable.
While there are challenges to overcome, the benefits of this approach are substantial, and it opens the door to a wide range of use cases across various industries. Whether in education, finance, business, or game development, the ability to define functional domain entities with YAML formulas provides a unique way to express and automate complex business logic.
As YAML continues to evolve and adapt, this innovative approach could become a valuable tool for those seeking a flexible, human-friendly way to define their domain entities and logic.