I see all the upside in entities registering automatically for basic components. If an entity has a Renderable component, it should be registered to a list that's used by a Renderable System. If an entity has a Physics component, it'll be registered to a list of physics entities that gets used by a Physics System.
But beyond that, say we have an entity that is created with both a Physics and a Renderable component.
I fully understand how ECSs normally handle this case; If there was a system that worked on entities that have both a Physics and Renderable component, most Entity Component Systems (ECSs) would then automatically register that Entity to that system because the signature matches (in other frameworks, if the aspect matches, etc).
Moreover, if you add any components from an entity at runtime, most ECSs will then loop through all systems and try to register this entity if its new signature matches the systems' requirements (similarly unregistering the entity when removing components).
In practice, this seems to have many downsides (specifically talking about cases of where a signature of MORE than 1 component auto matches a system):
1. All entities with a particular set of components will automatically register to a system that the programmer may not have expected/remembered. Perhaps it makes sense that an entity with a physics and a visual component should be operated on by a physics-visual system, but perhaps not. Every time we add components to an entity, it may be difficult to foresee all the systems that it'll register to and may have unexpected consequences (similarly for removing components). If we have multiple people working on creating systems, the likelihood of unexpected consequences goes up. [Logic errors at runtime, maintainability problems]
2. In the case that we have entities like the ones above that fit a signature but we don't want to register it to a particular system, the best solution revolves adding a tag to the entity. Then we'd add an exclusion to the aspect of the system that would have operated on it - but this couples the behaviour of EntityCreators and System registrations [High Coupling]. Otherwise, we'd check for a dynamic tag on each entity on the update loop of the system and perform a noop on certain tags [Unneeded work, not-exactly 'pure' ECS]
3. We need to create a bitmask system or some other equivalent to manage signatures, and always loop through almost all systems every time an entity changes. Of course, all of this depends on implementation, but some form of loop-check is always required. In many cases, for complex aspects or when dealing with a large number of systems, this can become super inefficient and constrain how you build up your aspects/systems. [Lots of Work]
4. Sometimes ECS is implemented in a way that allows systems access to all of an entities components which may or may not be part of the systems requirements. [easy to break Encapsulation]. Now this may not be a big deal if everyone's on board with the intricacies of 'pure' ECS, but given the number of questions surrounding type casting entities and checking entity tags at runtime, its common enough that it can lead to taking shortcuts in systems and creating more [Runtime Errors].
Really, problems 1, 2, and 4 produce problems inherent similar to those that appear with duck-typing and dynamic casting in general.
Instead of automatically registering entities to systems that match a signature (via whole entities or via nodes), wouldn't it be more type-safe and maintainable, yet as extensible, to directly register component-bag nodes in EntityCreators / EntityModifier classes?
If our entity which we create has a Physics component and a Renderable component, and matches in our minds what a PhysicsRenderable object does, then we would add a component called PhysicsRenderable which is constructed with our physics and renderable components injected. By explicitly saying that our entity matches what we believe a PhysicsRenderable should do, we solve 1.
If it doesn't match what we believe a PhysicsRenderable is, we only add the Physics and Renderable components, solving 2. Note: this does have the opposite effect of 1 that we have to remember to register it as a PhysicsRenderable if we want systems that work on PhysicsRenderable entities to update us, but this can be more easily debugged and fixed than the reverse situation.
Our entity would then have the components [Physics, Renderable, PhysicsRenderable]. Moreover our systems would then register to only one type (Physics, Renderable, PhysicsRenderable) and then the logic for registering the entity would become much simpler than using a bitmask or other method described in 3. And because we explicitly built the entity with a PhysicsRenderable, this can act as the type for the node-list traversed in our system that operates on PhysicsRenderables (no passing around full entities anymore, solving 4).
Added detail: removing a component on an entity that's composed in another component, ie, removing Physics from an entity with a PhysicsRenderable, would require an extra listener to also unregister the PhysicsRenderable component, but it's not much different, if not simpler, than what is already done in ECSs.
Now I realize that this goes against what some people may find to be a core principle of ECS (automatic signature registering), but it still uses a lot of the other core ideas (automatically registering entities to systems that process components that are expected on those entities).
Are there more downsides to this method that I'm not seeing? Do any ECSs use this sort of framework that requires explicit signatures on entities?