The German software engineer Ralf Westphal currently spreads some knowledge about an alternative model for programming components and especially communication between them. Due to their nature they are called Event-Based Components. After some discussion with colleagues at SDX I want to share some of my thoughts on that with you (of course for further discussion as well).
The aim of Event-Based Components (EBC) is to create software components that are really composable without specific topological dependencies. You can compare EBCs with elements in electronic circuits. But first things first…
Interface-Based Components style
Normally we’re developing components in .NET as IBCs: Interface-Based Components. That means client classes have topological and functional dependencies to interfaces (or directly to other classes), which provide some sort of functionality. Well developed, such a dependency could be resolved with a Dependency Injection container like StructureMap:
class Program { static void Main(string[] args) { // Bind (here: StructureMap) ObjectFactory.Initialize(x => { x.For<IBusinessClient>().Use<BusinessClient>(); x.For<IDataAccessComponent>().Use<DataAccessComponent>(); }); // Resolve and Run IBusinessClient client = ObjectFactory.GetInstance<IBusinessClient>(); client.BusinessOperation(0); } } interface IBusinessClient { void BusinessOperation(int personId); } class BusinessClient : IBusinessClient { private readonly IDataAccessComponent _dataAccessComponent; public BusinessClient(IDataAccessComponent dataAccessComponent) { _dataAccessComponent = dataAccessComponent; } public void BusinessOperation(int personId) { Person p = _dataAccessComponent.GetPerson(personId); // do something ... } } interface IDataAccessComponent { Person GetPerson(int id); } class DataAccessComponent : IDataAccessComponent { public Person GetPerson(int id) { return // ...some person... } }
That’s pretty standard so far, isn’t it? In Ralf’s opinion this programming style lacks real composability of the components. Due to the topological dependency the clients is bound to a specific interface and no arbitrary component can perform the functionality. Instead a component has to implement the specific interface. You’re not able to use components which could provide the functionality, but don’t implement the interface…
Event-Based Components style
Ralf suggests Event-Based Components to the rescue. Components in this programming style can be compared to components in electronic circuits. Methods act as input pins of a component and can be called by other components. Events/delegates act as output pins and establish a connection to other components that should be used by the component or to provide calculation results. The output pins can be bound to any arbitrary method that meet the signature. Thus the dependency is still functional, but not topological any more.
The example above in EBC style could look as follows:
class Program { static void Main(string[] args) { // Build var client = new BusinessClient(); var dataAccess = new DataAccessComponent(); // Bind client.GetPerson = dataAccess.GetPerson; // Run client.BusinessOperation(0); } } class BusinessClient { public Func<int, Person> GetPerson { get; set; } public void BusinessOperation(int personId) { Person p = GetPerson(personId); // do something ... } } class DataAccessComponent { public Person GetPerson(int id) { return // ... some person ... } }
This example shows the components BusinessClient
and DataAccessComponent
interacting as EBCs in a very simple form by using the Func<T>
delegate type and thus enabling symmetric communication. Ralf encourages the use of standard input/output pins as Action<object>
, which leads to asymmetric communication, because the DataAccessComponent
would need to declare an output pin for providing the Person
as result of GetPerson()
. For the sake of simplicity I haven’t followed this principle here.
So the example uses a Func<T>
delegate and no event. But you can still think of it as Event-Based Component, just because events are nothing more than multicast delegates. I could have used events instead of the simple delegate as well, but I’m quite fine, because I don’t need the functionality of multiple subscribers here.
As you can see from the example, just like IBCs the EBCs have some kind of initial Bootstrapper phase. This is the time when the components are composed. The output pins of a component’s client (BusinessClient
in this example) are connected with the input pins of the component itself (here: DataAccessComponent
).
Benefits
When I first saw EBCs I thought: „Dude, this is damn cool, isn’t it?“. Indeed this kind of programming style first feels strange and alternate and thus for me it’s really interesting. But are there some real benefits as well?
I think one big benefit of EBCs is their composability. A client hasn’t to know the interface of a component from which he wants to use some functionality. A component on the other side is not forced to implement an interface to provide some functionality, but it’s still retaining loose coupling. Even without interfaces the components are still independent from each other and have great testability.
Other benefits I see are the exchangeability and the topological independence. Components are not bound to a specific topological context in form of interfaces and thus are independent from topological changes on the interfaces. You can exchange the components easily by replacing the binding section with any other setup phase and can binding other methods to them. Especially your components are not forced to use (or implement) some kind of interface from which they will perhaps use just one single functionality…
Last but not least I see a very easy way to intercept calls and adding functionality without changing the components themselves. If you use events as output pins you can add some more event handlers in the binding phase. Thus you can easily integrate Logging, Tracing etc. into your applications. Of course you can achieve this with IBCs as well, I just say that EBCs are suiting very well for those requirements.
Drawbacks
Besides those benefits in my opinion there are some significant drawbacks as well.
First of all is the additional complexity which comes with EBCs. Composing EBCs can become complex, at least in projects of significant size. Due to binding methods and events together on the fine instead of interfaces on the coarse, there have to be much more binding statements. In fact you can think of an event’s signature as a one-method interface that has to be fulfilled from components. Furthermore (again especially in projects of a reasonable size) you will loose intelligibility and overview over your system and the component interaction. Any arbitrary component can provide a functionality and there is no way to navigate between layers and components as clients and suppliers of functionality. Explicit interfaces are much more comprehensive than such „implicit“ specifications. Perhaps in the future there will be tools that simplify composition between EBCs and navigation through EBCs, but until there’s such a tool I consider this as serious drawback.
Another drawback of EBCs is the loss of interfaces as formal contract of coherent functionality. Of course you can define interfaces and let your components implement them, but while clients are not compelled to use them they loose much of their value. Interfaces force components to implement a certain set of functionality completely and make this set explicit. Clients have to refer this contract explicitly. Explicit contracts lead to intention revealing and this is a good thing!
Conclusion
So in my opinion EBCs have benefits as well as shortcomings. I think they are worth investigating and knowing them, but at the moment I don’t see that they will establish well and become a replacement for IBCs. First there is the higher complexity, which could perhaps be solved by tools and some sort of „DI container“ for EBCs in the future. But second, being explicit and define formal contracts through explicit interfaces is no bad thing. Of course it’s not cheap as well, but I don’t see that this justifies the application of EBCs on the small scale. On the big scale there are other solutions like BizTalk, NServiceBus etc. to achieve the goal of pluggable components which have features like scalability as well. So perhaps there are delimited scenarios for using EBCs (like component topologies that change often), but I would not suggest to use them in general.