1. I want to be able to add/remove components in a consistent way no matter what the component. Having to locate the a system that happens to provide storage for them so that you can add/delete a component seems onerous. I want to just say "entity.AddComponent<Awesome>", or "world.AddComponentToEntity<Awesome>(entity)".
That seems very Unity-ish, which isn't at all what I have in mind for this sort of architecture. If you really don't want the system adding the component to know about the system that owns the component, you could queue up an event with the entity ID and the component type you want, then have the glue code process it, but I don't totally see the point of this, because you can always go systems.AwesomeComponentSystem.add(entity) or awesomeComponentSystem.add(entity) if you really want to, and not only is that about the same amount of code, but passing the world or the awesome component system makes the dependency explicit. That's a
good thing. That one system can create components of a type that it doesn't manage (ie. that live on another system)
is a dependency, no matter how much you decouple the two systems from each other.
In general, I want to be able to look at the header file for a system and know what it does. If it creates new components of a specific type, I'd like to be able to see that right up front without having to dig around in the code for it.
Why should code need to know which system happens to store the component?
Because generally the behaviour of the component goes on the system, not the component itself. By definition if a system knows about a component type, it will always know about that component's system, too, because it needs to know about the system to interact with the component. The component isn't really a "thing" in and of itself. It's just a piece of data that lives in a system. All the communication is between systems.
For instance, testing is made easier, because you can test a system in isolation. If I'm testing the "StarvationSystem", for instance, I don't need to fire up a HealthSystem and an InventorySystem just to do so.
What is the difference between queueing up a bunch of components in a list and passing it to a system, and queuing up a bunch of objects in the system that manages the component's state and passing the second to the first? Besides, most of the time the first system is going to interact with the components through the system that owns them, anyway, so you need to spin up the second system and pass it in regardless because it has the behaviour of the components.
Don't forget that with a system like this, the "components" themselves may not actually be monolithic objects. They may be separated out into structures of arrays for batch processing on the system that owns them. In the same way that "there is no game object", in a data-oriented system there may not be "components", either. My game's combat system, for instance, stores character attack timer state in a separate data structure from the hitboxes, and which characters are attacking is stored in. Here's the state that system stores:
TypedInstancePool<ObjectType::Character, CharacterCombatState> m_combatState;
TypedInstancePool<ObjectType::Character, sf::FloatRect> m_hitboxes;
std::vector<TypedHandle> m_lungingCharacters;
std::vector<TypedHandle> m_parryingCharacters;
std::vector<Attack> m_attackQueue;
std::vector<Block> m_activeBlockZones;
Again, though, YMMV on whether this (or anything I'm talking about) is really a pure ECS. As already noted, ECS is prone to "no true scotsman"-type arguments.