What is a component?
For the purpose of this project the term component
is defined as
- binary functional unit
- with a separate contract
Specifically that means methods and classes are not components. They are functional units, but they are not binary. Binary functional units on the .NET platform are assemblies.
Assemblies are not components by themselves, though. They lack a separate contract. So what needs to be added is some kind of machine readable description of the services of an assembly to arrive at a component. One easy way to define such a contract is to
put contractual types in a second assembly.
Example: A compiler translates a mathematical formula given as a string (e.g. "x^2 + 3*x - 7") into an executable function with signatur
A compiler component would then look like this: It would consist of a contract...
public interface IMathCompiler
Func<double, double> Compile(string mathExpression);
... and at least one implementation ...
public class CodeDomCompiler : IMathCompiler
public Func<double, double> Compile(string mathExpression)
Separating contract and implementation like this has several advantages:
- Once the contracts of all components in an application are defined (Contract-First Design) their implementation can be done in parallel. This increases productivity.
- Tests can be written against contracts and be given to the developers implementing the components. This helps in distributed develpoment scenarios to ensure only high quality code is delivered back. Such tests define behavior (semantic contract)
in addition to the syntactic definitions in the contract assembly.
- Separate contracts make implementations independent of certain implementations. If component C depends on component S, then it´s statically bound only to the contract of S. This allows for different contract implementations, eg. for mock-ups during
testing or for different technologies (eg. a CodeDom based expression compiler or a handcrafted one or an ANTLR based one).
Example of static dependency on contract only:
// references mathcompiler.contract.dll
public class Calculator
private IMathCompiler compiler; // static dependency on contract type
public Calculator(IMathCompiler compiler) // ctor dependency injection
this.compiler = compiler;
public IEnumerable<PointF> Calculate(string mathExpression, ...)
var f = this.compiler.Compile(mathExpression); // dynamic dependency
for(double x = ...)
double y = f(x);
yield return ...;
of a component often consists of interfaces like above. But enums, structs, and classes are allowed in contracts, too. However, in order to be able to reap all the benefits from Contract-First Design you should keep contracts free of implementation
details. As soon as you start to introduce statements into contracts so that you need to test contractual code, you´re making contract development slow. That should be avoided. Contracts need to be coded very quickly as to not hinder hinder their parallel
of a component is the sum of all its contracts, ie. the contract it implements (exported contract
) and the contracts it depends on (imported contracts