Derived attributes & associations

Derived attributes has been in ECO for ages . To a SQL-guy derived attributes resemble calculated fields. Derived attributes are just like calculated fields that subscribe to all the values that it is calculated from. So whenever data, seen by the expression, is changed the derived attribute is marked as out of date. And the next time you or your UI tries to read it, the attribute is re-evaluated.

The key thing with derived attributes is that it is NOT calculated each time you access the attribute. If it were performance would suffer. It is calculated (or derived) as few times as theoretical possibly; only when read the first time after a change of anything that the derivation expression looks at.

The concept of derivation relies on the concept of subscription – everything in your domain layer has the ability to signal whenever it is changed (the publisher pattern), and subscribers subscribe to publishers to detect these changes. Although this is a different story I must mention that publishing events to catch changes is a key difference between the ECO approach to implementing a domain layer and the POCO way (plain old c#-objects) like NHibernate and some other frameworks use.

image

Having a model as the one above I can create a derived attribute on the Order that calculates the total shipping cost by checking the products ordered and the customer’s country in OCL:

self.OrderItems->FilterOnType(ProductThatNeedsShipping).Weight->sum
* self.Customer.Country.CostToShipHerePerKilogram

I do this by creating a new Attribute, set the AttributeMode to Derived and fill in the DerivationOCL.

Derived attributes can be used in other derivation expressions so I can make a derived TotalCost without repeating the definition of the Shipping cost:

self.OrderItems.Product.Price->Sum() + self.ShippingCost

The expression of derived attributes can be expressed with C# / VB.NET / Delphi.NET code instead of in OCL (ECO only, not Gaffr.net). You do this by setting the AttributeMode to Derived as before but leave the DerivationOCL property empty. Generate code and inspect the Order.eco.cs file. At the bottom of the file you will see these stubs:

Code Snippet
  1. /// <summary>This method is called when ShippingCost needs to be calculated</summary>
  2. partial void ShippingCostDerive(ref string res);
  3. /// <summary>This method is called when TotalCost needs to be calculated</summary>
  4. partial void TotalCostDerive(ref bool res);

You never change the files named something.eco.cs – instead you implement these partial methods in the file something.cs (or vb or delphi). So in our case we open up the Order.cs file and enter:

Code Snippet
  1. partial void ShippingCostDerive(ref double res)
  2. {
  3.     res=0;
  4.     double totWeight=0;
  5.     foreach(OrderItem i in OrderItems)
  6.     {
  7.         if (i is ProductThatNeedsShipping)
  8.         {
  9.           totWeight += (i as ProductThatNeedsShipping).Weight;
  10.         }
  11.     }
  12.     if (Customer!=null && Customer.Country!=null)
  13.       res = totWeight * Customer.Country.CostToShipHerePerKilogram;
  14. }

Prior to ECO5 you needed to explicitly explain to ECO what ECO should subscribe to in order to stay updated if anything changes. But as of the introduction of the IAutoSubscriptionService this is no longer necessary.

The IAutoSubscribeService deserves a chapter of its own but here is a brief overview:

The caller of your code derivation has a subscriber and starts an Auto subscription session. An Auto subscription session is notified about all access to the domain layer as long as the session is in effect. All access is inspected and builds up the description of what ECO needs to subscribe to in order to know when to mark the attribute out of date. The session is ended after your code derivation returns.

Conceptually this is what happens:

Code Snippet
  1. using (this.AsIObject().ServiceProvider.GetEcoService<IAutoSubscriptionService>().StartSubscribe(subscriber))
  2. {
  3.    double res;
  4.    this.ShippingCostDerive(ref res)
  5. }

All you need to remember

All you need to remember is that derived attributes can be defined in code or in the model with OCL. Derived attributes are efficient and always return the correct up to date result. Associations can also be derived the same way as attributes.

In the example model above the Shipping cost is out of date if you add a new orderline, if you change the customer, if the customer changes country, if a already picked ProductThatNeedsShipping gets an updated weight etc – it covers every and any change that effects the calculation as long as that change is part of your domain layer.

I hope that you see the positive effect this will have on your UI implementations – Showing the ShippingCost and see it update as you change anything it depends on by one central definition far away from the UI.

Using derived attributes and associations clean up your code and consolidates central definitions to single points and thus greatly reduce maintenance costs and efforts.

This entry was posted in Derive, UML, UMLSchool. Bookmark the permalink.

One Response to Derived attributes & associations

  1. Pingback: Short introduction to UML– class diagram | CapableObjects

Leave a Reply

Your email address will not be published. Required fields are marked *

*