What is ECO anyway?

By: Peter Morris

Abstract: This article provides an overview of ECO

What is ECO Anyway?

I remember when I first heard about Bold for Delphi, I didn't really know what it was so I emailed BoldSoft to ask. Either Jonas or Jesper sent me a document they had just written explaining it. The document went on to describe blind men encountering an elephant, each one feeling a different part of the animal and assuming it was something different (a tree, snake, etc). Well, that made it as clear as mud, so I promise not to mention blind men or elephants from this point onwards.

This article will give an overview of the abilities of ECO but will not describe how to use them, it is meant only to explain what ECO is so that you may decide for yourself if you would like to investigate further. This article will not explain UML. If you are not familiar with some very basic concepts such as Classes and Associations then I recommend you do a small amount of preparatory reading on that subject first.

Index ECO is an application framework

A very brief way to summarise ECO is to say that it is an application framework, by this I mean that it helps you to build applications quickly. Delphi itself is allows you to create applications quickly, so what is so different about ECO?

Delphi provides lots of tools for creating nice looking applications, especially when additionally using third party extensions, but when it comes to the business layer there is still a lot of work to do. Probably everyone knows that it is bad practise to mix your GUI code in with your business logic, yet many people still do it. People who do not mix their presentation and business logic either have to do a lot of manual work creating database tables, stored procedures, etc; or have previously spent a lot of time writing their own library to map objects to a database.

Forgetting for a minute all of the nice additional services of ECO, you could say that this essentially is what ECO does for you. ECO allows you to define your business layers as a collection of associated classes. These classes remain completely separate from any GUI code, and may even be compiled into their own re-usable .net assembly, making it easy to reuse the business logic for multiple user interfaces (WinForms, WebServices, Web Applications, Import / Export routines).

Working with objects

Instead of selecting data from tables, and inserting new rows into tables, you now work purely with objects. For example

[SQL]
insert into 
  Customer (Name, Telephone)
values
  ('Air Software Ltd','+44 (0)121 243 6689');
[Pascal]
  NewCustomer := Customer.Create(EcoSpace);
  NewCustomer.Name := 'Air Software Ltd';
  NewCustomer.Telephone := '+44 (0)121 243 6689';
  EcoSpace.UpdateDatabase;
[C#]
  Customer newCustomer = new Customer(EcoSpace);
  newCustomer.Name = "Air Software Ltd";
  newCustomer.Telephone = "+44 (0)121 243 6689";
  EcoSpace.UpdateDatabase();

To create a related object is just as simple.

[Pascal]
  AlternateTelephoneNumber := TelephoneNumber.Create(EcoSpace);
  AlternateTelephoneNumber.Name := 'Home number';
  AlternateTelephoneNumber.Number := '+44 (0)121 999 9999';
  NewCustomer.ContactDetails.Add(AlternateTelephoneNumber);
  EcoSpace.UpdateDatabase;
[C#]
  TelephoneNumber alternateTelephoneNumber = new TelephoneNumber(EcoSpace);
  alternateTelephoneNumber.Name := "Home number";
  alternateTelephoneNumber.Number = "+44 (0)121 999 9999";
  newCustomer.ContactDetails.Add(alternateTelephoneNumber);
  EcoSpace.UpdateDatabase();
The benefits of working with objects

The above examples show how to manipulate your data as "Business objects" rather than as data, but why would you want to do this? Probably the main reason for taking the OOP approach is the ability to use Polymorphism.

In the above example I used "ContactDetails.Add()" to add the alternate telephone number. You may assume that this is merely a list of telephone numbers, but why should it be? Take a look at the following UML diagram.

Contact details

In this diagram you can see that a Customer has zero or many ContactDetails. ContactDetails has only a single attribute, "Name", it is also an abstract class so instances of ContactDetails may not be created. From ContactDetails I have descended an EmailAddress class that has an "Address" attribute, and a TelephoneNumber class that has a "Number" attribute, I have also modelled a FaxNumber class which is a type of TelephoneNumber. This allows me to record various types of contact information for a customer, and group them all together under the generic name of "ContactDetails".

Of course objects may also have virtual methods, which is probably one of the most powerful features of OOP. By using object inheritance it is possible to modify the default behaviour of our objects.

Polymorphism

The above example is not meant to be a real-life one, there are reasons why I wouldn't take this exact approach that aren't really relevant to this article. The purpose of the above model is to demonstrate polymorphic behaviour.

In the model you will see that we can enter a list of computers on our network. Periodically these computers check what tasks they have to perform. To do this they do the following

  1. Locate a computer object with their own unique name
  2. Call ExecuteScheduledEvents

ExecuteScheduledEvents will do the following

  1. Get a list of all related "ScheduledEvents" that have a NextDueDate <= Now
  2. Call ExecuteActions on each

A ScheduledEvent has one or more tasks, these tasks may belong to zero or many ScheduledActions so are easily reusable. The Scheduled event will simply call Action.Execute() on each action in its list. This list of actions is ordered, so the order of the actions is predictable.

What is not predictable from the context of the ScheduledEvent is exactly what Action.Execute() will do. All ScheduledEvent has is a list of object instances which it knows have a certain abstract class as their base type, concrete descendant classes determine the action action performed. A ScheduledEvent may do something like this

  1. ScanDisk
  2. DefragDisk
  3. BackupDatabase
OOP summary

Having code execution within your business model really does introduce a lot of power to your application. The addition of introducing OOP and polymorphism allows you to additionally model your business classes in a more abstract way, which is always a good way of planning for future enhancements.

What is an EcoSpace?

The EcoSpace is an in-memory object cache of your object instances. The EcoSpace does not start by loading all of your database into memory, instead it employs lazy-fetch techniques that I will describe later.

The EcoSpace is your application's way of managing instances of your business classes. EcoSpaces do not belong in a .net assembly along with your business classes, instead you should have an EcoSpace within your application. At design-time you are able to tell the EcoSpace which package(s) it will be working with, ECO determines the list of available packages by searching the current project + used assemblies for UML packages containing your modelled classes.

Object persistence

ECO does not use the term "Database" for a very good reason. You don't have to save your objects to a database. For example, there is an XML persistence component that allows you to persist your object instances to disk, another "persistence mapper" will update your data to a remote ECO powered application using remoting, or you could allow your application to connect directly to the database and use a BDP or SQL persistence mapper.

NOTE: XML persistence should not be used in released software, it is for testing only

Once you have specified which classes the EcoSpace will be managing, and also configured your persistence settings, it is now possible to generate your database schema. Using the default settings it is possible to create your database with the single click of a button; all of the tables and columns required to store data about your class instances are generated instantly.

Alternatively you have two more persistence options

  1. You may enter custom mapping information to map your instances to an existing database
  2. You may reverse engineer an existing database in order to create your business model

My favourite feature of ECO persistence mapping is without doubt the database evolution support. When you later revise your model structure + update your application you will also need to modify the structure of your database. It is rarely acceptable to wipe your database clean and regenerate because there may be data help within it. This is where database evolution comes in. Database evolution modifies your database schema without losing your data, database evolution can handle the following changes to your model.

  • Renamed classes
  • Renamed attributes
  • Renamed associations
  • Moving an attribute/association up or down a class hierarchy

An example of renaming a class "Preson" to "Person", renaming an attribute "FristName" to "FirstName" within the same table, and then moving an attribute "DateOfBirth" from a descendant class into Person would produce steps something like this

  1. Create table Person
  2. Move data from Preson to Person
  3. Drop table Preson
  4. Create column Person.FirstName
  5. Move data from Person.FristName to Person.FirstName
  6. Drop column Person.FristName
  7. Create column Person.DateOfBirth
  8. Move data from Employee.DateOfBirth to Person.DateOfBirth
  9. Drop column Employee.DateOfBirth

Of course moving an attribute up the class tree has no code implications in your source code, because Employee objects still have a DateOfBirth attribute due to inheritance.

ECO can create your database structure, it can map its objects to your existing database structure, it can evolve your database structure (even if it was not created by ECO), and can even reverse engineer your current database structure into business classes. Additionally it is possible to persist your objects to non-database storages by implementing your own PersistenceMapper component.

Object caching and lazy-fetching

Once an object instance has been fetched from the persistence storage its values are cached within the EcoSpace. Whenever you modify the attribute values (properties) the cache is updated. Even if your object instance in source-code goes out of scope and is subsequently garbage collected, ECO will hold a cache of its attribute values in memory. The next time you retrieve your object instance using this EcoSpace, the EcoSpace will return an object based on this cache rather than requiring a persistence request.

As I mentioned earlier, ECO does not start its life by fetching all of your objects from the database, although by looking at the following source code you would be forgiven for believing it did.

Assume that only "MyCustomer" is currently loaded

[Pascal]
  for ContactDetails in MyCustomer.ContactDetails do
    ContactDetails.Name := ContactDetails.Name + '!';
[C#]
  foreach(ContactDetails contactDetails in MyCustomer.ContactDetails)
    contactDetails.Name += "!";

So, if only MyCustomer is loaded, how is it that you don't have to explicitly tell ECO to load all of the objects in the ContactDetails association?

ECO holds a cache of object instances plus their properties. Whenever you read or write a property of one of these object instances the read/write is actually passed through to the EcoSpace cache. This approach has a number of benefits, a few of them are

  • Objects are not re-fetched from the persistence storage, they are recreated from the cache, this saves time
  • Requesting an object instance from the EcoSpace multiple times results in the same instance being returned from the cache, so updating a property in one reflects in them all
  • The EcoSpace is able to automatically fetch required data from the persistence storage if it doesn't currently have a cache entry for it

In the example code above an attempt is made to read MyCustomer.ContactDetails. Multi-links are not fetched with objects when they are retrieved from the persistence storage, so the EcoSpace requests a list of object identifiers from the persistence storage service. As you then access each ContactDetails instance within MyCustomer.ContactDetails again the EcoSpace will issue a request to the persistence storage service to retrieve that object.

NOTE: If you are going to loop through a known list of objects it is possible to instruct ECO to pre-load those objects in advance as economically as possible

Delay-fetched attributes

Some of your modelled classes may have attributes that are expensive to retrieve. A binary attribute holding a passport photo could be considered expensive, both to transfer over the network and to hold in memory. It would be unwise to retrieve such an attribute every time you loaded a Person object for example, because in most situations your software will probably be working with a Person in ways that do not require anybody to look at their photograph.

ECO allows you to mark attributes in your classes as Delay-fetch. When an object instance is retrieved from the persistence storage any attributes identified in this way are not retrieved. Only when you try to access the value of the attribute will ECO issue a request to the persistence storage service to retrieve it. This approach allows you to keep memory and network usage down.

NOTE: It is currently not possible to unload an individual attribute, however, it is possible to discard an object instance's cache so that its values are reloaded when next accessed

The object constraint language

Some people refer to the OCL as "SQL for objects", which isn't a bad comparison. OCL is used throughout ECO applications, both in your business model, and also on your forms / WebForms, and in your source code too.

This overview article will not describe OCL in any great detail, the language is too large, but I will describe some common uses for OCL and also explain what the OCL expression I have used means. For more information on OCL you should consider reading through Anthony Richardson's excellent article (originally written for Bold for Delphi) here w ww.ViewPointSA.com

OCL in your business model

Within the model itself there are a number of places in which OCL may be used.

Object constraints

It is possible to add a list of name/expression elements to any class within your model. ECO does nothing with this list internally, but it is possible to use this constraint list to ensure object validity. For example, a class implementing a hire period may have the following two constraints

  • Start date required : not startDate.isNull
  • End date must be after start date : endDate > startDate

I always use such constraints in my model. I am able to enable or disable the Save button on my WinForms depending on whether or not all of the constraints of the current object have been met or not, or I can use the ASP .net ValidationSummary to display messages about broken constraints on the current object and prohibit saving.

Whenever new rules are required, I update the constraints in my model and redistribute it. I don't need to recompile my WinForm app or my website project, and the exact same rules are applied to both interfaces.

Derived attributes / associations

Dataset programmers are familiar with calculated fields. Derived attributes/associations are similar except for a few important points

  1. The member is a valid part of the model, so is accessible via code or any WinForm/WebForm. Unlike a Dataset it does not have to be implemented each time you want to use it.
  2. The member is lazy-calculated. Some calculations may be expensive to perform, so ECO will only calculate the value if you attempt to access its value in some way
  3. The result of the calculation is stored in the EcoSpace's cache. This saves it from being recalculated each time. Every element used in the calculation is "subscribed" to, so if any of the variable values involved alter the cached value is marked as invalid.

NOTE: It is not only possible to calculate derived attributes / associations' values using OCL, you may also calculate them by implementing certain methods in your class

Take a look at the following UML diagram.

Article categories

This is a real-life example of an ECO model actually in use. I used this structure on www.HowToDoThings.com. I have indicated all derived attributes / associations by colouring them red (associations) or a nice pink colour (attributes).

This UML structure is used to categorise articles on my website. According to the diagram, articles on my website can belong to a single SubCategory. A subcategory must have exactly one parent, which is a BaseCategory, this enables a Subcategory to be parented either by another SubCategory or a MainCategory (which has no parent because it is a root category).

SubCategory has an attribute named "ArticleCount", which is an Integer. The OCL for this attribute is

articles->size

"articles" is a member of SubCategory, it is a one-to-many association linking a SubCategory to multiple articles. The expression "articles->size" tells ECO that we want to know how many articles there are, note that this does not load the articles into the ECO space. Adding a new article, deleting an article, or moving an article to a new SubCategory will automatically mark the cached result of this OCL invalid.

A huge benefit of having these "calculated fields" actually within the model comes from now being able to use these attributes in OCL as well. Looking further down the SubCategory class you will see an attribute named "TotalArticleCount". Where ArticleCount shows the number of articles in this category, TotalArticleCount will show the number of articles belonging to this SubCategory and the total number of articles of any SubCategory beneath this point. This is achieved with the following, very simple, OCL expression

articleCount + subCategories.totalArticleCount->sum

This expression is so simple because it uses a clever recursion trick.

  1. Count how many articles I own directly
  2. Add to this the "TotalArticleCount" of each of my child SubCategories

The "TotalArticleCount" of each of the subcategories does exactly the same, it returns the number of articles it owns directly + the sum of all "TotalArticleCount" attributes of its child SubCategories. This recursion continues until eventually you reach the leaf nodes in the structure and there are no more SubCategories, at this point the expression "subCategories" returns nil, and therefore the result is calculated as

articleCount + (zero)
Derivation expressions

Derivation expressions are an easy way of overriding an expression of an inherited OCL-derived attribute or association. Against each class it is possible to enter a list of name/expression pair values identifying which member you want to override, and its new OCL.

In an application I wrote recently I had a model in which different users manage many "things". There were many different types of "things" within the model, the "InterestedParties" for each depended on what the "thing" was and what state it was currently in (live, pending, cancelled). Any user of the system could send a message/request to a relevant user by seeing who its InterestedParties were. An example would have been the User class itself.

[Thing]
InterestedParties=User.emptyList
[User]
InterestedParties=Administrator.allInstances

NOTE: "Administrator" is the class name, "allInstances" is an OCL operation on a class to retrieve all instances.

This is a very powerful approach because I have the full power of OCL to determine would should be available to deal with queries/requests relating to an object.

OCL in your source code

It is also possible to execute OCL statements within your source code using the OCL service. This technique is useful for obtaining references to objects, or for evaluating the result of a complex expression that would otherwise require many lines of code.

ECO handles

This section will be very brief. I merely want to reassure people that normally use Dataset and grids etc that they do not suddenly need to start depending completely on writing source-code.

ECO handles are components that may be dropped onto your WinForm/WebForm at design time. These components provide access to the "ECO world". For example, the ExpressionHandle allows you to enter an expression such as "Customer.allInstances", this expression retrieves all Customer object instances.

These handles support standard .net databinding, so it is now possible to databind visual controls to the handle to allow the user to edit the attributes of the objects. From a user's point of view there is no difference between editing ECO objects and rows from a Dataset.

ECO Services

The EcoSpace provides multiple services that provide you with some additional features. Some of these services are very simple, some of them are very powerful. The EcoSpace will return an instance of a particular type when requested.

[Pascal]
PS := EcoSpace.GetEcoService( typeof(IPersistenceService) ) as IPersistenceService;
[C#]
ps = (IPersistenceService) EcoSpace.GetEcoService( typeof(IPersistenceService) );

Some of these services are summarised below.

Persistence service

The persistence service allows you do perform the following actions

  • Apply all changes to the persistence storage
  • Apply all changes for a specified list of objects
  • Retrieve objects using a criteria
  • Discard ECO cache values for an instance
  • Retrieve changes made by other users
Dirty list service

The dirty list service tracks all modified (dirty) objects. You can obtain a list of all dirty objects in the EcoSpace using this service.

Extent service

This service provides the developer with the ability to work with all instances of a particular class, it performs the following actions

  • Retrieve all instances of a specific class
  • Receive notifications whenever an object is added/deleted
External ID service

This service allows you to obtain the unique ID of a particular object instance, or to object an object instance from the EcoSpace based on its unique ID.

OCL service

This service allows you to execute OCL statements from within your source code and retrieve the result. It also allows you to optionally subscribe to the OCL statement so that you receive a notification when any of the elements in the expression change. This service can also be useful for retrieving objects from the persistence service. See the article Two functions to evaluate OCL on www.HowToDoThings.com.

State service

The state service merely provides the ability to determine whether a particular object instance is modified or not.

Undo service

The undo service allows you to programmatically start a named "undo block". Whenever an object instance is modified, the EcoSpace will automatically store its old value in the active UndoBlock (if there is one). It is then possible at a later point to undo your changes by reversing the changes in the undo block, it is also possible to then redo those changes.

The undo service also provides in-memory object change transactions. This is basically identical to the undo service, except you do only use StartTransaction(), CommitTransaction(), or RollbackTransaction() rather than having to work with named undo blocks.

Version service

Have you ever seen the WayBack machine? This is a website that spiders other sites, whenever the site changes the new pages are cached, enabling you to look at a website at any given date.

This is similar to the version service in ECO. In ECO you can identify a class as being versioned. Each time modifications are persisted they are saved as a new version of the object. Whenever you request that object ECO will return the latest version. Using OCL it is possible to navigate through objects and associations at any given date and time, so it is possible to see historical data.

The version service provides information about versioned classes such as

  • Determine the current version number of an object instance
  • Retrieve a historical instance of an object
  • Determine the date/time of a specific instance
Summary

As you can see, ECO is not UML, and is so much more than an object persistence framework. There is quite a lot of information in this article about ECO, yet I suspect that there is probably a lot I have missed out, for example, I haven't explained the following

  1. Optimistic locking
  2. Object regions
  3. Multi-user synchronisation

The latter is covered in an excellent article by Roland Kossow here. I may write follow up articles on the first two at a later date.

Hopefully this article has given you some idea of "What" ECO is, as opposed to "how" to use it. I also hope that it has been successful in arousing your interest in this excellent development tool, and you will consider looking into the "how" part a little more.

There are various people in the borland.public.delphi.modeldrivenarchitecutre.eco newsgroup willing to help you with any questions you may have, and also a dedicated ECO section on www.HowToDoThings.com.

About the author

Peter Morris has been developing model-driven applications since his introduction to Bold for Delphi (ECO's predecessor) back in 2001. He is the managing director of Air Software Ltd, a company that provides bespoke software solutions and consultancy services to various industries.

Elephant, blind man.



Server Response from: ETNASC02