Anyways, the implementation is broken into 3 distinct pieces:
- The entity that is a common aggregation of functionality.
- Components that are individual composable units of functionality.
- And a definition that is immutable and readonly. It defines what an entity is, and is used by the components to determine if the component is applicable, and if so, what flavor of component to use.
This project requires the components be deployable independently of each other. They're more of a plugin at this point than conventional components. To make this work, components are separated into parts. There is a public interface that defines the component, and should be stable over time. And then there is concrete implementation(s), including a factory class for component discovery. There may also be component specific data/assets. The consumer of the entity, as well as other components only ever refer to the DLL having the public interface.
At composition time, the core code uses reflection (project requirements prevent our use of MEF, Unity and a few other IoC containers) to hunt for types that inherit from the common factory interface for component discovery. The Definition is then passed into the factories to spin up instances of all the components (where applicable).
To get the components to talk to one another, the design calls for dependency injection. A common attribute exists that components can use to tag their fields/properties as a dependency. Once all of the components are created, a second step goes through all of them looking for the attribute. When one is found, it looks for a component instance that inherits from the tagged field/property. If one is found, reflection is used to assign the instance into the other component. They then talk directly to do their communication.
Reflection is okay here, since we have few actual entities and they're created infrequently. Direct communication is okay too, since our components are pretty tightly coupled (in a business sense) when coupled. They won't live on different threads/machines, so abstracting the communication only leads to unnecessary complexity.
And that's it in the nutshell. We've not actually implemented too much of it yet, so it might be garbage; we'll see. Hopefully that helps. If anything is unclear, please ask and I'll be happy to elaborate as much as I can.