ECO 2 under the hood

One of the bigger changes in ECO 2 is not immediately visible to the casual beholder; it is on the inside. We have broken the monolithic EcoSpace into collaborating components to enable an open and extensible architecture. In this post I will attempt to outline that architecture. But first, allow me to take a step back to set the context properly.

Think of ECO as organized into layers, with each layer representing a particular category of tasks you, the developer, can perform with ECO. The presentation layer – which is also the first you’re likely to have come in contact with when starting with ECO – is designed to deal with matters of, well, presenting data to the user. The different handles, such as the ExpressionHandle and the RootHandle are in this layer. The business layer is where you put your business logic. It contains the modeled classes and the code you write in them and that uses them. The service layer consists of the ECO services, such as the IPersistenceService and the IUndoService. It is designed to deal with your application choices, such as when and what to save to the database.

The topic of this post is the framework layer. It consists of the framework components and the interfaces used to connect them. This layer allows you to modify and extend the behaviour of the EcoSpace itself.

Time to look at a picture!

This diagram shows some of the main components and interfaces of the framework layer. The DefaultEcoSpaceImpl component implements several of the capabilities of the EcoSpace. In particular, it implements one of the more important capabilities, caching, which it exposes through the ICache interface. This interface, along with its implementation, is responsible for holding the state of the business objects – their attribute values, object id, etc – in memory. In other words, this is where the actual value (e.g. “John”) of the Name attribute of an instance of your Person class is stored.

The FrontsidePolicy component provides the implementation of the interfaces in the Borland.Eco.ObjectRepresentation namespace, such as IElement, IObject and IProperty. While the cache has a fine-grained, technical view of objects, the FrontsidePolicy implements the high-level behaviour of the ECO objects. For instance, cascading delete is implemented in the FrontsidePolicy. When IObject.Delete() is called, the composite associations (or, more accurately, those with DeleteAction.Cascade) of the object will be traversed and the objects traversed will be marked in the cache with ExistenceState.Deleted. Another example is delayed fetch of an attribute. Say a piece of your business logic reads the Name attribute of a Person:

if somePerson.Name = “John” then…
When the attribute is read, the FrontsidePolicy will check in the cache if it has been loaded from the database. If not, it will ask the persistence handler, as exposed by the IPersistenceHandler interface, to fetch the attribute. The persistence handler places the fetched value in the cache, where the FrontsidePolicy reads it and returns it to the calling business logic.


The persistence mapper on the diagram is the same component that you place on an EcoSpace to allow the objects to be saved. Examples include the PersistenceMapperBdp and PersistenceMapperXml. The role of the persistence handler, mentioned above, is to bridge between the persistence mapper and the cache. When saving to the database, it will extract the changes from the cache and package them for a call to the persistence mapper’s Update() method. Similarly, when data needs to be fetched it will make the appropriate call to the persistence mapper, inspect the returned data package, and make the required modifications to the cache state. This way, the cache has no dependency on the persistence mapper, and the persistence mapper has no dependency on the cache.


If I were you, at this point I’d be saying to myself “Alright, this is all very interesting, but what does it have to do with me?”. So, if I were me, which I am, I’d be trying to answer that. See those arrows in the diagram? You can rewire them. See those interface circles? You can implement them. See those boxes? You can replace them. Have a look at the class DefaultEcoSpace, which you’ll find in the file /Source/Eco/Handles/EcoSpace.cs. It is this class that your EcoSpace inherits from. I have extracted the parts that directly correspond to the diagram above:

    private EcoSpaceImpl m_ecoSpaceImplementation = new EcoSpaceImpl();
[snip]
private FrontsidePolicy m_frontsidePolicy = new FrontsidePolicy();
[snip]
public virtual void Initialize()
{
[snip]
m_ecoSpaceImplementation.ObjectRepresentationProvider = m_frontsidePolicy.ObjectRepresentationProvider;
[snip]
m_frontsidePolicy.Cache = m_ecoSpaceImplementation.UndoCache;
[snip]
m_frontsidePolicy.PersistenceHandler = m_ecoSpaceImplementation.PersistenceHandler;
[snip]
m_ecoServiceProvider.RegisterEcoService(typeof(IObjectFactoryService), m_frontsidePolicy.ObjectFactoryService);
[snip]
m_ecoServiceProvider.RegisterEcoService(typeof(IPersistenceService), m_ecoSpaceImplementation.PersistenceService);
[snip]
}

Notice that Initialize() is virtual, so if you want to you can override it in your own EcoSpace and set things up as you like. Some things you can do:



  • Add your own services.
  • Replace the default implementation of an ECO service with your own.
  • Insert a pass-through implementation of one of the interfaces (such as ICache) to inject your own behaviour. This technique is demonstrated in the ChangeLogging and LazyFe