Capability Driven Design

Peter Harrison - Feb 9 '23 - - Dev Community

What is Capability Driven Design?

Capability Driven Design places the focus on software units which deliver very specific capabilities to perform certain general function, but that do not contain constraints unrelated to that capability.

An example might be a database server, which has the job of storing and retrieving data, but is not constrained to store data of a particular structure. The schema of the data can be modified at runtime. You don't need to modify, build and redeploy a database to add a field to a table for example.

Another example are Spreadsheet applications which can be used to store data and make calculations that are complex, but are not tied to any particular model or data. They are used across all kinds of businesses for enumerable applications.

These examples provide capabilities that have broad utility, but never the less have a very specific purpose and scope.

  • Value building robust adaptable capabilities over short term narrow tactical objectives.
  • Value universal services that do not contain unnecessary constraints.
  • Value applications that have a well defined scope and mission over applications that have accreted over time.

Capability based Micro-services

While there is nothing specific about the capability based approach that ties it to micro-services, this is one area which could benefit greatly from a capability based approach over the more traditional domain driven approach.

With micro-services we are developing each service to deliver a specific capability, rather than serve a particular aspect of the domain.

When we decompose a system by domain we will end up with numerous databases, each with their own schema. This in itself creates challenges with being able to transform data across these boundaries. This also results in a services which are fundamentally similar in capability, that is storing and retrieving data, but requiring substantial instances to run, and hops across multiple servers to service requests.

Micro-services decomposed by domain

This picture changes when we decompose by capability. All data can be handled by one code base, meaning cross cutting concerns like security can be handled consistently. It reduces platform complexity as the domain complexity is now in the configuration rather than leaking out into the infrastructure itself.

Image description

Considerations:

  • Decompose systems based on required capabilities rather than the splitting the data schema
  • Don't duplicate capabilities across micro-services
  • Use public standard API where possible
  • Avoid complex call paths to minimize latency

Advantages

  • Lower resource use
  • Fewer connections, less latency
  • Lower system complexity

Example

Imagine you have an application where there is a customer record. You need to add a new field, say a email address. How will this go with a Domain Driven Design vs a Capability Driven Design?

With Domain Driven Design you will need to do some database changes:

ALTER TABLE customer ADD email_address char(50);
Enter fullscreen mode Exit fullscreen mode

Some code changes to the REST service

public class Customer {
    private String name;
    private String phone;
    private String email;
Enter fullscreen mode Exit fullscreen mode

And finally modify the front end code

<form>
    <input name="name">
    <input name="phone">
    <input name="email">
</form>
Enter fullscreen mode Exit fullscreen mode

Not to mention potentially changing any reports or screens that display the customer. And this is a trivial change.

Now we turn to a Capability Driven Design where the domain is stored in a database and can be easily modified using a web based tool.

If the service has the domain in configuration adding the additional field is as easy as editing the schema in the online tool, and the new field will take effect right away.

Schema Editor

There is no need to add the new field in multiple locations. There is no need to alter unit tests as no code has changed. The user interface now dynamically includes the new field. Once records are added with the new field populated it is also possible to search on the new field, again with no need to write as much as a line of code.

Domain Decoupling

The key feature of the Capability Driven Design is that the domain in ejected from the source code. This is the polar opposite to Domain Driven Development where the focus is on the domain first, and consequently the system becomes brittle to change.

To be crystal clear, this does not mean the domain no longer exists, but rather it resides somewhere else that can be modified dynamically without redeployment. Ideally there are easy to use administration tools which can be used to make modifications at runtime by skilled business analysts.

What does this accomplish?

  • Improves Business Flexibility and Adaptability
  • Empowers business analysts and managers.
  • Enabled rapid change without risky deployments.
  • Results in less code, with less cost.
  • Code that is written has far broader use.
  • More secure and robust.

What drives coupling?

If a capability based approach works so well why don't we already do it? The first reason is that the traditional way of developing code has been domain-centric because databases, while dynamic, generally have static schemas that are difficult to change once in production. New document based approaches to data storage are more recent.

Second, a capability approach requires a longer time horizon. It is faster and more expedient to hack together something that works than to spend time decomposing the system into separate well defined capabilities. Over time this pattern of an accumulation of expedient changes to a code base to deliver more and more varied functionality results in what is known as a 'Big Ball of Mud'.

Database schemas that are difficult to modify in production means that these too can be a source of resistance to change. When change is painful it is avoided. Naturally there are tools for migration, but synchronization of binaries and database schema updates are still a challenge that introduces friction.

Tied to the fixed schema is often a object mapping to code which directly reflects the tables in the database. Essentially now every table has its own object which is used to create, modify and retrieve data. But what happens when you want to add a field, or even add an entire table?

Finally applications are also where business rules are enforced. Having business rules mixed together with the general code base for an application makes changing the business rules difficult. They are generally no located in one clearly defined area. But business rules are different for each business, and change over time. Ideally these should also be moved to configuration.

How to avoid coupling

Use a Document based Data Store like MongoDB

There are a number of options today that offer a document based storage. Even SQL products like Postgresql and Oracle now support JSON documents, which open the door to a more flexible approach to data storage compared to traditional SQL.

Expel Object Model Mapping

Systems like Hibernate and many others do a good job of mapping from the database schema to classes that represent that schema. There is much to like about these tools, as it makes life easy, greatly reducing or perhaps even eliminating the need to write SQL yourself. Bad news is that these tools will certainly bind you to a static data model. Time to go cold turkey and push the domain into configuration.

Dynamically generate queries

This is simply the consequence of the above; if you push the schema into configuration you will need a way to generate SQL queries from the configuration. Or, if using MongoDB you will need to dynamically generate MQL.

Define business rules in configuration

Business rules or business processes that are strongly tied to the details of the schema should also be pushed in configuration, so that they can be modified at runtime. Java has javascript and Python engines which can run dynamic code similar to Lambdas.

Conclusion

Developing services that employ Capability Driven Design principles are more difficult to develop initially. They are also more complex in a certain way, as they must dynamically generate queries, forms, screens and reports. The advantage is that you do it once, and the code is reused for any future similar case.

Also, while I have presented Capability Driven Design in opposition to Domain Driven Design, in reality the line between capabilities and domain won't be clear.

However, having employed this approach and seen its success I think it is worthwhile expounding it to show there a Domain Driven approach is not the only approach, and in fact leads to inflexibility. Expedient change over time only leads to a complex unmaintainable code base that becomes a liability for future adaptability and business growth.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .