Some month ago I asked myself the same question. Since my data orientated engine makes good progress, I can give you some advice.
Generally speaking, the key is to separate data and function. This way components can be connected only by the data they modify instead of being connected directly with each other.
Each component inherits a
Init() and a
Update() function from an abstract base class and that's it. The components neither know of each other nor do they call other's functions. Moreover component don't store data as class members but in global storages. This results in nearly stateless components, the state is given by the global data.
As I said, data is stored globally so that every component can fetch and modify the same data. Therefore I use a global data manager. It allows to create entities, which are nothing but an global number, and assign properties to them. There properties can be position data, buffers, health points, and so on. One important thing is here for the data manager to provide a function to fetch all properties of the same type. This allows a component to process all properties related to itself. For example the physics component fetches all position properties, loops over them, performs collision checks and updates the coordinates. The renderer component fetches the same data along with other properties like mesh buffers and materials to draw each form at the right place.
As a developer you are very flexible in such a architecture. Since there are no direct dependencies between components, you could for example just remove the physics component. The code would compile successfully and all other modules would still work as expected. Only the global position data won't be updated anymore and the scene stands still. As you might see this also allows you to change component, that means separate functionalities, with easy. The new component just have to modify the same data storages than the one before.
It might sound elaborate to developers who prefer object oriented design to get rid of all dependencies between components. But indeed it results in very encapsulated, easy to maintain and fast code. I told you that my own little game engine is based on this architecture. If you are interested, feel free to visit the Github repository take a look around.
As a side note, this architecture isn't guaranteed to be data oriented in the narrow sense of the word. It definitely uses that paradigm, that means data is stored in long lists by property and not by object. But it depends on the implementation whether the structure of your global data storages actually fit the CPU cache or not.
You might also want to read this question about data driven design in game engines.