Derived attributes & associations

Derived attributes have been in MDriven for ages. To an SQL guy, derived attributes resemble calculated fields. Derived attributes are like calculated fields that subscribe to all the values they are calculated from. Whenever data is seen by the expression and changed, the derived attribute is marked as out-of-date. The next time you or your UI tries to read it, the attribute is re-evaluated.

The key thing with derived attributes is that they are NOT calculated each time you access an attribute. If such were the case, the performance would suffer. It is calculated (or derived) as few times as theoretically possible - 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 can 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.

ModelOrder+Customer.png

Having a model like 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, setting the AttributeMode to Derived, and filling 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 (MDriven for Visual Studio). Do this by setting the AttributeMode to Derived as before but leaving 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

/// <summary>This method is called when ShippingCost needs to be calculated</summary> 
partial void ShippingCostDerive(ref string res);
 /// <summary>This method is called when TotalCost needs to be calculated</summary>
 partial void TotalCostDerive(ref bool res);

Never change the files named something.eco.cs. Instead, implement these partial methods in the file something.cs (or vb or Delphi). So in this case, open up the Order.cs file and enter:

Code Snippet

partial void ShippingCostDerive(ref double res)
 {
   res=0;
   double totWeight=0;
   foreach(OrderItem i in OrderItems)
   {
       if (i is ProductThatNeedsShipping)
         {
           totWeight += (i as ProductThatNeedsShipping).Weight;
       }
   }
   if (Customer!=null && Customer.Country!=null)
   res = totWeight * Customer.Country.CostToShipHerePerKilogram;
 }

Exception

Please note that because of how time flows, you can't subscribe to time, like DateTime. Now, if it could, it would always need updating and lead to an infinite update loop.

See also the Default String Representation and asString which are not subscribed.

What 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 in the same way as attributes.

In the example model above, the Shipping cost is out of date if: you add a new order line or change the customer, or the customer changes country, or an already picked product that needs shipping gets an updated weight, etc. It covers every and any change that affects 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 shipping cost and seeing it update as you change anything it depends on by one central definition far away from the UI.

Using derived attributes and associations cleans up your code and consolidates central definitions to single points and thus, greatly reducing maintenance costs and efforts. Having the definition in OCL also makes the derivation a part of the documentation – the model – rather than just the implementation.

See also:

C# and the AutoSubscribeService

In versions of the product ECO, you needed to explicitly explain to ECO what ECO should subscribe to stay updated if anything changes. Since 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

using (this.AsIObject().ServiceProvider.GetEcoService<IAutoSubscriptionService>().StartSubscribe(subscriber))
 {
   double res;
   this.ShippingCostDerive(ref res)
 }

The MDriven Book - Next Chapter: UML – State machines

This page was edited 29 days ago on 06/17/2024. What links here