Creating the Perfect DDS Data Model Using Component Based Modeling

rti-blog-post-image-2018-11-29-component-based-modeling-642x396-1118

Modeling data for DDS systems can be challenging. There is a seemingly never-ending list of variables to take into account: memory usage, message overhead, discovery, quality of service, evolvability, application logic and so on. Often projects have too much emphasis on one aspect, which can lead to suboptimal solutions and expensive redesigns down the road.

While there is no silver bullet to creating the perfect data model, there are best practices that increase the probability that a model meets requirements with limited cost of change. In this blog, I will provide an introduction to one of the methodologies I present to customers in quickstarts and architecture studies: component based modeling. For you to get the most out of this blog, a basic knowledge about topics, instances and the OMG® IDL is recommended.

Before going into specifics, let me bring up one other thing. An important and often overlooked data modeling consideration is how applications interact with DDS. Teams typically design domain specific layers on top of DDS that encode patterns and hide complexity. While necessary, these layers often are expensive sources of technical debt, as they are built when knowledge about DDS is still developing and system requirements are still forming. Component based design, as we will see, offers a more well-defined approach to designing this layer.

So what is component based development? Let’s look at an example. When we model data, we usually think in objects: we define what an object is, create a class, and collect its relevant attributes. Following this approach, we would intuitively model a “Car” like this in IDL:

struct Car {

     @Key string vin;

     string color;

     long latitude;

     long longitude;

     long speed;

     boolean door_sensors[4];

};

At first glance, there seems to be nothing wrong with this approach, and for many situations, this works just fine. For a DDS system however, this type is suboptimal:

  • Some members are periodic, some aperiodic
  • Some members are static for the entire lifecycle of the instance
  • Members are likely written by different subsystems

 Even for a simple type, the intuitive “object oriented” way of modeling data does not necessarily produce the best results. This problem gets bigger as models get larger.

Component based design, as opposed to object oriented design, models what an object has, instead of what an object is. We will see in a bit how component based design helps us create better IDL models. First, let’s take a look at components. A component is all data that belongs to a “capability” of our class. From our Car type, we could derive the following components:

  • Color (color)
  • Position (latitude, longitude)
  • Speed (speed)
  • DoorSensors (door_sensors)

Note I am using a different naming convention for components, so I can unambiguously talk about a component versus a member. Also note that nowhere in this list of components does it say explicitly that we are describing a car (though we could take an educated guess). These components could indeed be reused for all sorts of different objects, which is a powerful capability that comes in handy when designing the rest of the system.

Now that we have a list of components, we can start thinking about how we want to derive our topic types from them. One approach would be to create a topic per component. Following this approach, we would get six topics that look like this:

struct CarSpeed {

     @Key string vin;

     long speed;

};

Note the “vin” key: we need this to know that data from these different topics (CarColor, CarPosition, CarSpeed, CarDoorSensors) all describe the same car.

While this would give our applications a very fine-grained way to publish and subscribe for data, it is still not very efficient. Because the types are so small, the relative overhead of the DDS protocol header would be quite large. Also, managing and recombining data from six different DDS readers would be cumbersome. Instead, what we do is recombine components based on the Quality of Service of the data. This is an example of how we could do this for the Car:

  • Static data (Color)
  • Periodic data (Position, Speed)
  • Aperiodic data (DoorSensor)

This would result in the following types (and topics):

struct CarMovement {

     @Key string vin;

     long latitude;

     long longitude;

     long speed;

};

 

struct CarColor {

     @Key string vin;

     string color;

};

 

struct CarDoorSensor {

     @Key string vin;

     boolean door_sensors[4];

};

Now that DDS is happy, we still need to address the application layer. As it turns out, we can use the same methodology, but instead of grouping by QoS, we group components by application logic. The following diagram illustrates how components can be derived from DDS and then recombined for the different parts of an application:

null

  

This approach has many advantages, but in particular, two especially desirable characteristics:

  • The application model is a straightforward mapping from the DDS (IDL) model which reduces complexity in the layer.
  • The application and DDS models can evolve separately in a lot of scenarios, which can be a big advantage, especially in early stages of a project.

If you’re looking for ideas on how such a component layer can be realized, I recommend reading about entity component systems and checking out one of the many ECS open source implementations for inspiration.

I hope this gave you some ideas on how to design your DDS data models. As always, if you have questions or need help in designing models for your specific use case, do not hesitate to reach out to the RTI Professional Services team!

 

Getting Started with Connext DDS

Connext® DDS is the world's leading implementation of the Data Distribution Service (DDS) standard for Real-Time Systems. Try a fully-functional version of Connext DDS 5.3 for 30 days.


Free Trial