Functional requirements change frequently. System architecture is difficult to change. If system architecture depends upon functional requirements, then the system is going to be brittle and expensive. Instead, system architecture should only be based on non-functional requirements. It should be isolated from changes in functional requirements.
The difference between functional and non-functional requirements
Functional requirements describe how the system behaves within the problem domain. A banking application will express its functional requirements in terms of customers, accounts, and transactions – financial transactions, not database transactions. If a branch manager decides to offer a free checking account with every home mortgage, then he is changing functional requirements.
Non-functional requirements describe how the system behaves from a technical perspective. They are independent of the problem domain. Whether it’s a banking application, a healthcare application, or a property management system, non-functional requirements are expressed in the same terms. If data needs to be replicated to a different location, that’s a non-functional requirement. If records should be denormalized for reporting, that’s a non-functional requirement. If the branch manager wants to launch a new web site, he is changing non-functional requirements.
Web services
Too many of our foundational systems conflate functional and non-functional requirements. The result is software that is difficult and costly to change. Take, for example, web services. Every web service defines two things:
- A message contract
- A delivery mechanism
The message contract describes what is to be sent from the client to the server, and what is to be returned from the server to the client. The contents of a message are elements of the problem domain. A banking application will have web services to list a customer’s accounts, and to get transactions by account and date range. These are functional terms. When functional requirements change, the message contract has to change.
The delivery mechanism describes how messages are sent between the client and the server. Practically speaking, web services use HTTP. They are synchronous in nature, and initiated by the client. The message contract is defined by the server. Message delivery is not guaranteed. These are the non-functional consequences of choosing web services.
A web service conflates the functional and non-functional requirements through its use of the Web Service Definition Language (WSDL). WSDL contains a definition of the message contract, a functional construct. When functional requirements change, the WSDL is updated. The server must publish the new WSDL for all of its clients to consume. The non-functional relationship between clients and servers is now dependent upon functional requirements expressed in the message contract.
Relational databases
Another example of conflated requirements can be found in a relational database. Relational databases require developers to define a schema, which describes tables and columns. Tables are entities in the domain, and columns are attributes of those entities. A schema is dependent upon functional requirements. When the functional requirements change, the schema changes.
A relational database has tools to help us satisfy non-functional requirements. Non-functional requirements will describe the Service Level Agreement (SLA) governing how quickly transactions must be processed. They will also describe how frequently reports are run. Based on these requirements, a DBA may choose to index the data differently. They may choose to replicate from the transactional database to the reporting database. They may even decide to denormalize the reporting database for better performance. All of these decisions are non-functional in nature.
Indexing, replication, and denormalization all depend upon the database schema. When the schema changes, these decisions must be revisited. The schema changes whenever functional requirements change. As a result, expensive architectural decisions are affected by quickly changing functional requirements.
This can only lead to one of two outcomes:
- Changes to functional requirements are expensive, or
- Changes to functional requirements are discouraged.
Either outcome is death to business.
Application-agnostic architectures
To allow functional requirements to change frequently, architectural decisions should be completely isolated from the problem domain. If an architectural decision depends upon a functional requirement, then it runs the risk of becoming invalidated by future requirements changes. Architectural decisions should therefore be made with knowledge of non-functional requirements alone.
To some, this may sound like Big Architecture Up Front. Well, it is Up Front, for all the reasons described above. But it doesn’t have to be big. We generally know before getting into the details of the problem domain whether we will need a web application, a reporting database, or a service bus. The up front architectural decisions will simply select these components based on non-functional requirements. You may even decide to use technologies like web services or relational database that conflate requirements. Just be careful that you don’t centralize these technologies, allowing them to cause friction as functional requirements change.
One consequence of isolating architectural decisions from the problem domain is that architecture can be amortized. Since architecture does not depend upon the problem domain, it can be done up front with no knowledge of the application. Transaction processing is a common non-functional requirement. Reporting is a common non-functional requirement. A good architecture can be crafted for performing transaction processing and reporting with no knowledge of the applications for which it might eventually be applied. That architecture can then be used in many different applications, with no additional cost.
It is common for a consultant to answer a question with “it depends”. If the question is an architectural one, then “it depends” should be followed with a non-functional requirement. If architectural decisions depend upon functional requirements, then you will be paying that consultant over and over again as you make changes to your business model. But if we as an industry create a ready-made set of application-agnostic architectures, we can pull the right one from the shelf and apply it to a large number of problem domains. If these architectures are carefully constructed not to conflate requirements, then they can drive down costs over time.
Disagree. Depend on
Disagree. Depend on abstractions not concrete. The higher level the component, the more abstract. Also, the check out the dependency inversion principle.