triangulum-db


Dictionary-based database upgrader and biz logic code generator

Documentation

To Skip right to the docs, go to Using Triangulum.

Status

As of Feb 5, 2011, we are in early adopters stage, having exited hacker preview. Any database schemas you create using Alpha 3 will be supported in the beta stage and through to Release 1.

Use Alpha 3 if the core features supported in it are sufficient for your current needs, and you do not require a fixed delivery date for more features.

Alpha 3 was uploaded the morning of Saturday Feb 5, 2011, Eastern Time. The wiki is being updated to reflect new features, and this notice will be removed once the wiki is fully updated.

Purpose And Goal

Triangulum is not Yet-Another-Database-Upgrade tool, though it can fill that role.

Triangulum's purpose, goal and focus is to simplify the management of business logic. In so doing, Triangulum reduces development time, reduces the size of development teams, reduces coding overall, makes business logic more transparent and makes changes to business logic easier and simpler.

All of this is done by allowing the developer(s) to decompose certain types of business logic, specifically derived values, into column and table definitions. With all derived values specified in terms of the tables they come from, the application code becomes simpler, as it is reduced to two major elements. First is simple "transfer functions" between the database and user interface, and second is more complex compound operations that need only process sets of inserts, updates and deletes with no special coding for the calculations.

An Example

Consider a simple case of three tables, Orders, OrderLines, and Items. To compute the order total, we might have a SQL query that joins the three tables, calculates Items.price OrderLines.qty, and sums this value.

When using Triangulum, we decompose this business logic into its intermediate steps, each of which is specified separately, and each of which is materialized into the tables on top of the normalized values that are supplied by users.

The basic design method for this case would go like this:

  • I need a column in Orders, Orders.LinesTotal, representing the final answer
  • This column, Orders.LinesTotal, must sum something from OrderLines
  • That value it sums from OrderLines would be OrderLines.ExtendedPrice (a new value)
  • The formula for OrderLines.ExtendedPrice would be Price **qty, but
  • I do not have Price in the OrderLines table, so I will "fetch" it from

    Items to OrderLines**

This results in a Triangulum database definition that looks something like this:

```

This is a "module" don't worry about that right now

sales: # Inside the module are tables, this is the items table items: sku: type: char(10) keys: primary price: type: numb(10,2)

# This is the orders table. It should have a foreign # key to customers, but this example leaves it out # for simplicity orders: orderid: # This is a shortcut to make an IDENTITY Primary Key type: identity linestotal: type: numb(10,2) # This is the Triangulum magic auto: sum(orderlines.extendedprice)

# This is the orderlines table orderlines: orderlineid: type: identity # This is how we do a foreign key. Notice we do not # repeat the columns, Triangulum puts the pk column(s) # of the parent table in here for us. So we will # get "orderid" and "sku" in our table. fk_orders: parent: orders fk_items: parent: items # The user-entered value qty: type: smallint

# More Triangulum magic, get the price into context
price:
  type: numb(10,2)
  auto: fetch(items.price)

# Final step, calculate the extended price.  This is an
# unconditional calculation, conditionals are also supported
extendedprice:
  type: numb(10,2)
  # Triangulum recognizes columns from the "!"
  calculate: !price * !qty

```

We now have a simple database specification in which our business logic for the Orders.LinesTotal has been decomposed and specified declaratively. Every insert to OrderLines will automatically grab the price and update the Order. The three derived columns, OrderLines.price, OrderLines.extendedprice and Orders.LinesTotal are now always updated and correct, and available for reporting or display on the UI with no table-specific code required.

It is this process of automatically managing these calculated values that reduces the coding burden for projects that use Triangulum.

Arbitrary Complexity

The example given above is deliberately over-simplified to illustrate Triangulum's method, but more complex cases are easily handled by the same simple method, decomposing them into their smallest units and adding derived columns to tables to support calculations to any depth of complexity.

A Note About Implementation

Release 1 of Triangulum will materialize all derived values as columns in tables. This has the great advantage of making a Triangulum-built database accessible from any client, freeing developers from the One True Application architecture that requires the application to constantly grow to support every possible access use case.

This implementation is an example of a "pay me now" design trade-off, adding cost to each INSERT or UPDATE for the benefit of vastly simplified code, access from any client, and an easily reportable database.

However, some architects may prefer a "pay me later" approach, where the values are not materialized, but are calculated on the fly when retrieved. Once Release 1 is complete we will begin to explore other strategies for this, including but not limited to generating views instead of materializing columns, a mix of materialized columns and views, generating application-level class code for ORM systems, and anything else that may present itself.

Use in Higher Tiers

Once Triangulum has built a database, it generates descriptions of all tables in multiple formats, including JSON and YAML (XML is not generated at this time, but would be trivial to add if requested). These dictionary files can be used by library code in the application to validate operations on any table. This again eliminates the need for lots of table-specific code, because the dictionary tells you everything you need to know about what is going on in the table, so general-purpose library code can handle operations like pre-validating and building an INSERT, determining which fields on a UI form are read-only, and many other tasks.

Project Information

Labels:
Database CodeGeneration