My experience with ECS is that you shouldn't use an "ECS system". You should just do ECS. Have an array of all your particles and then update all the positions according to the velocities. Don't use a framework where you do something like get_all_entities_with<Particle, Position, Velocity>(). Just have struct particles {vector<vec3> positions, velocities;}. Well, managing those parallel arrays gets pretty annoying, but you solve that with a parallel-array class template rather than a whole framework that promises to do everything. (You might not actually need parallel arrays anyway - AoS might work just fine here because you usually touch each field of each particle once per frame and exactly in order.)
The problem is that there are a lot of subtleties to an ECS that these frameworks solve, and they perform better than a naive approach too. Your solution of a particles struct doesn’t even support a fundamental feature of ECS’s which is runtime composition. It’s really a different solution altogether, which is fine but it’s not a replacement for an ECS.
Often you don't have runtime composition and don't need it. By prematurely generalizing you're venturing close to OOP territory.