Imagine we have a system for managing users. Say, within the system source code, two separate arrays hold information on users. One array contains unique identifiers for users, represented by integer ids:
user ids:
[ 1, 7, 9, 4, 12 ]
The second array contains the names of the users:
user names:
[ "Dave", "Carol", "Alice", "Eddie", "Bruce" ]
The elements at a given index correspond to the same user’s information in both arrays. Therefore, user “Alice” at (zero-based) index 2 in the names array has a user id of 9, also at index 2, in the user ids array.
The order of the elements in both arrays must be synchronised. If we remove the user with id = 9 at position 2 within the user ids array, we must remove it from the user names array also at position 2. The resulting arrays would see id = 9 and name = “Alice” spliced from both arrays at position 2:
user ids:
[ 1, 7, 4, 12 ]
user names:
[ "Dave", "Carol", "Eddie", "Bruce" ]
Can you see the problems we might run into with this flawed design?
What will happen when another programmer, unaware of the implicit relationship between the arrays, decides to reorder the user names in alphabetical order? Maybe they want to display the user names in alphabetic order in the UI:
reordered user names:
[ "Alice", "Bruce", "Carol", "Dave", "Eddie" ]
Yet the user ids array remains unchanged (!):
user ids:
[ 1, 7, 9, 4, 12 ]
Now, when we remove id = 9 at index 2, “Carol”—rather than “Alice”—will be deleted from the user names array. Oops!
Not only are we removing the wrong user name (“Carol”), but we are also retaining the user name we should have removed (“Alice”).
Why does this problem come about?
Even though the intent for these two arrays is to keep the ordering of the elements synchronised, there exists no mechanism to ensure this.
OK, how do we fix this?
A better way is to house related user ids and names together in an object:
Why is this better? By combining the related user information into objects removes the need for order synchronisation between two arrays. If we want to delete the user with id = 9, we do so, and the entire user object disappears:
users with { “id”: 9, “name”: “Alice” } in position 2 removed:
“Gather together those things that change for the same reason, and separate those things that change for different reasons.”
When it comes to the SRP, many of us—myself included—primarily consider the things we should separate—like UI & database. Yet, it’s just as important, if not more so, to place together functions and data structures that change at the same time and for the same reasons—like our user ids and user names.
Say we are working with a messy legacy software system where high-level business logic is intermingled with data access code. The business logic hardly ever changes—it is stable. Conversely, the data access code is modified often to reflect changes to the database schema—it’s volatile. Since data access and business logic are united, business logic code is also affected whenever we modify data access logic. Business logic has become volatile by being closely associated with the unstable data access code.
We have a system where we must make more changes than we would like. And fewer changes are better.
OK, what might be a better system configuration?
It would be one where we affect fewer lines of code whenever we make a change.
Let’s consider two different 1,000 line systems:
System 1 is the one we have been considering all along. Business logic and data access logic are inseparably joined. Let’s assume changes always affect 10% of code in this system. Also, 80% of changes are to volatile data access code and 20% to stable business logic.
Under these conditions, how many lines of code do we modify for the average requirements changes? (For the sake of simplicity, I am assuming that we are adding no new code):
Lines changed: 1,000 lines * 10% = 100 lines.
In System 1, the average code change is 100 lines.
In another system, System 2, Business logic is separated from data access logic. There exists a one-way dependency from data access logic onto business logic. So, data access logic could be affected when we alter business logic, yet business logic is isolated from data access logic changes.
The question came up again. What shall we call a type converting from a source type to a destination type? Say, from an Order to a DbOrder? How about OrderToDbOrderConverter?
Wouldn’t that mean that other source inputs producing the same destination object, DbOrder, required another converter class? Say SalesInvoiceAndPurchaseStatusToDbOrderConverter? That’s a rather long name.
With conversions, we are primarily interested in the resultant type—here DbOrder. That’s the type we need to carry on with our program.
So, I suggest that we name converters after the output type. If we are converting to DbOrder then the converter type would shorten to DbOrderConverter. For ease of use, all the methods become overloads of Convert() or Map(). The source conversion types become the input parameters.
For example:
public class DbOrderConverter { public DbOrder Convert(Order order) { // Convert from Order to DbOrder } public DbOrder Convert(SalesInvoice salesInvoice, PurchaseStatus purchaseStatus) { // Convert from SalesInvoice & PurchaseStatus to DbOrder } }
The only caveat with this approach is that we carefully construct converters to be aware of only one mechanism at a time. For example, if a converter knows how to convert both an ApiOrder, a web type, and a DbOrder, a database persistence type, to an Order, a business logic type, then this is a problem.
Why?
Consider this: Where would such a converter type be located? Within the database project? Now the database project would have to know about web! What about the web project? Not any better—the web project would need to reference and become coupled to the database project.
What is required here would be two separate converters, each serving one adjacent transition between architectural layers.
Functions describe system behaviour. They are actions. And as such, a function’s name should describe the work it performs.
What do these functions do?
InvertedImage()
Transaction()
Processed()
TotalAmountWithTax()
These function names are a bit ambiguous.
On the other hand, these function names are more descriptive:
ProcessExpenses()
ReconcileTranactions()
GetCustomer()
BuildVehicle()
The starting verb makes all the difference. The noun is also helpful—it describes the object of the behaviour; what the action is affecting. The noun is not always necessary. Sometimes it’s obvious. A File class with an Open() method will be about opening the file.
The verb ought to be in the present tense. Simple Present Tense, e.g. CreateGame(), is more suited for naming functions than Present Continuous Tense, e.g. CreatingGame().
Are there exceptions to the rule of putting the verb first?
Yes, there are.
Constructors: Constructors are functions setting up the initial state of an object. In many languages, the constructor name must be the name of the class:
public class ChessGame() { // Constructor public ChessGame() { } }
Properties: In C#, we can have property getter and setter functions. We call these without parentheses, and they resemble data more than behaviour:
var totalAmount = ShoppingCart.GetTotal;
or
var totalAmount = ShoppingCart.Total;
The second option looks more natural. The first line confuses without invoking parentheses.
Well, not hugely wrong. But still not entirely correct as I see it. This inconsistency has been bothering me for a little while. It’s not a big deal but something to be aware of.
And this flaw does not take away from the utility of Clean Architecture as a software engineering philosophy in any substantive way. I still firmly believe that Clean Architecture—for all its abstractness and potential flaw—leads to beautifully simple application designs that can be easily modified and extended. What’s not to love?
All right, let’s get into it.
Here is the familiar schematic diagram for Clean Architecture.
Clean Architecture
The Dependency Rule states that inner circles cannot know about outer ones. On the other hand, outer shells must depend on inner ones. In the end, some circles must know about one another, or it’s not a productive program but merely chunks of unconnected code.
So, outer rings know about inner ones, and the little arrows depict this one-way relationship:
Use Cases know about Entities—Entities are unaware of Use Cases.
Interface Adapters know about Business Logic—Business Logic is ignorant of Interface Adapters.
Frameworks & Devices are aware of Interface Adapters, yet Interface Adapters are oblivious to Frameworks & Devices.
I have a problem with the last statement. I believe it to be wrong.
Why?
Let’s assume the statement is correct and that Frameworks really are dependent on Interface Adapters.
Say, in a system, we use SQL Server as our database technology.
It would mean that our generic SQL Server data access SDK should reference, or depend on, our specific data access code to retrieve data from our SQL Server database!
Inversely, our specific SQL Server data access could not call generic SQL Server data access functions to fetch or save data since there exists no reference! How is our application-specific data access mean to do its job?
In my opinion, the dependency should be the other way around between the outermost layers.
Interface Adapters depend on Frameworks & Drivers (red arrow).
The Interface Adapters shell connects interfaces exposed by Business Logic with those of the general code of Frameworks, libraries and SDKs.
Adapters act as connectors between two dissimilar interfaces. To see as much, take a look at that adapter plug you’re using to connect the toaster/TV/microwave you bought in the UK to your power outlet. It has both a male and female plug interface. The toaster does not need to know about the UK adapter, and neither does your power outlet—neither depends on the adapter. On the other hand, the power adapter depends on both UK and local power outlet interfaces.
Adapters always depend on both interfaces. Yet, the things that an adapter connects do not, by themselves, depend on the adapter.
Adapters depend on both interfaces.
Therefore, it stands to reason that within the context of Clean Architecture, Interface Adapters depend on Business Logic interfaces and the outermost Frameworks circle.
That’s it. I hope that makes sense.
What do you think? Am I missing something? Feel free to comment or send your opinion to olaf@codecoach.co.nz. I want to learn and see where I am going wrong! :-)
In the last post on Clean Architecture, we explored how caching could be implemented in a pluggable manner within the Interface Adapters layer.
Here is the diagram of the data retrieval part of the system:
I have highlighted the interface connecting from CachedCustomerRepository to the database. However, there is a problem.
Again here is the listing for CachedCustomerRepository:
public class CachedCustomerRepository : ICustomerRepository { private ICache<Customer> Cache { get; set; } private ICustomerDatabase Database { get; set; }
public CachedCustomerRepository(ICache<Customer> cache, ICustomerDatabase database) { Cache = cache; Database = database; } public Customer GetCustomer(string emailAddress) { var customer = Cache.Get(emailAddress); if (customer != null) return customer; customer = Database.GetCustomer(emailAddress); if (customer == null) return null; // Put the customer into the cache for future calls. Cache.Set(customer.EmailAddress, customer); return customer; } }
Notice how the interface to the database is named ICustomerDatabase.
As I see it, we can do improve this design.
As it stands, if we wanted to revert to a simpler system, one without the caching infrastructure and rip out CachedCustomerRepository, so that we can connect up the SqlCustomerRepository to the use case, then we would be in trouble. The use case uses ICustomerRepository, yet SqlCustomerRepository implements ICustomerDatabase!
To make this work, could we have our business logic make its data calls against ICustomerDatabase?
We don’t want the business logic to be aware it’s calling a database—the use case should utilise a neutral interface like ICustomerRepository.
How about SqlCustomerRepository implementing ICustomerRepository instead of ICustomerDatabase?
Let’s try that out. Here is what the system diagram will look like without caching:
Yes, the system is pluggable—we can unplug SqlCustomerRepository and plug in a different database implementation.
OK, what about if we reinstate caching? Here is the system diagram:
Isn’t that nice?! The entire caching infrastructure, especially the logic module switching between cache and database data retrieval, CachedCustomerRepository, plugged upstream into the use case and downstream into the SqlCustomerRepository modules. It just connects up!
Technically, the new implementation of CachedCustomerRepository is a proxy, a segment that sits between two interfaces of the same type. Proxy is a valuable design pattern—it allows us to slip behaviour between an interface consumer and interface implementer.
Our new versatile system, whereby database implementation, caching implementation and the data retrieval workflow are pluggable modules, resembles how Lego blocks connect to one another. Elegant Software Architecture is highly pluggable—in the right way.
Clean Architecture – Caching With Interface Adapters
Last time we discovered the versatility of Clean Architecture’s Interface Adapters shell and how it acts as a connecting layer between the central Business Logic and our system’s specific technologies—the Frameworks & Devices.
In the example, we had business logic that wrote to and read from a SQL Server database. The code that lets us save and retrieve data from the specific database schema belongs in the Interface Adapters. Let’s check out the design we ended up with last time:
The advantage of this design is that when the database schema changes, our SQL Customer Repository Adapter will reflect those changes yet leave our Business Logic unaffected. Here we have powerful pluggability.
OK, let’s make things more interesting.
We want to introduce an optimisation—data caching. Instead of every data read running off to the database, we want first to check whether the data exists in a cache. If it does, return the data and do not read from the database. If not, go to the database, read the data, and put it in the cache for subsequent reads.
Now, where should the logic reading either from the cache or the database live? What about the entirely separate logic connecting us to a specific Redic cache implementation?
Do these belong to the business logic?
No, our business logic is the wrong place. These are both data concerns. All the business logic is concerned with is a way to read this data—that’s it. How the data is retrieved is not its concern.
OK, does this logic belong to the general SQL Server and Redis caching code? i.e. in the Frameworks & Devices shell?
No, that would also be incorrect. We don’t want to mix specific and generic data access.
On the other hand, it makes sense to have the code connecting our specific data retrieval from Redis cache (i.e. construction of cache keys, etc.) in a module at the same level as our specific SQL Server data retrieval code, SQL Customer Repository Adapter.
Furthermore, the logic switching between cache and database reads must sit in front of, and connect to, both the cache and database modules.
OK, so a picture is forming as to a system design that includes caching:
We now have two layers of logic in Interface Adapters—firstly, a module to switch between reading data from cache or database. Secondly, the adapters to retrieve data from Redis and SQL Server.
The logic to switch between cache and database is abstract. It does not mention Redis or SQL Server as the given cache or database technologies. Why? We get the flexibility to plug in other caching and database technologies.
Here is an example implementation of a class managing the Customer data retrieval logic, first from a cache and then from a database:
public class CachedCustomerRepository : ICustomerRepository { private ICache<Customer> Cache { get; set; } private ICustomerDatabase Database { get; set; } public CachedCustomerRepository(ICache<Customer> cache, ICustomerDatabase database) { Cache = cache; Database = database; } public Customer GetCustomer(string emailAddress) { var customer = Cache.Get(emailAddress); if (customer != null) return customer; customer = Database.GetCustomer(emailAddress); if (customer == null) return null; // Put the customer into the cache for future calls. Cache.Set(customer.EmailAddress, customer); return customer; } }
What we have here is a decent pluggable design. However, we can improve on it further by making a small change. We’ll look into that next time.
I can’t wait until next time when we will increase the capability of our Interface Adapters layer by introducing data caching.
OK, let’s get into it.
Say we have a system that we have designed with Clean Architecture guidelines in mind.
There is some business logic, a use case class, that needs access to customer data, and it gets this via an ICustomerRepository interface:
public interface ICustomerRepository { Customer GetCustomer(string emailAddress); }
Note: For the sake of simplicity, all the calls are synchronous.
Customer data is stored in a SQL Server database table called Customers. We have generic SQL Server framework code to save and retrieve data from SQL Server databases in general. This generalised framework code belongs in the outermost Frameworks & Devices shell of our Clean Architecture diagram.
OK, where do we put the connecting code, the behaviour, that utilises the generalised SQL Server framework but is about our specific database and how we store and retrieve customer data from it? Behaviour like reading the database connection string from the application-specific configuration; reading data from the Customers table, and possibly some other joined tables.
Maybe such functionality belongs to the Business Logic?
Business Logic is meant to be ignorant of data mechanisms like SQL Server. Referencing SQL data access code aware of our database type and internal schema suggests intimately knowing data mechanisms. No, we can’t put this code into the Business Logic layer.
What about the Frameworks & Devices shell? Well, it doesn’t here either. This outermost layer schematically holds generalised frameworks and is, in the context of SQL Server, concerned with generic SQL data access.
Our SQL Server database-specific code belongs to the Interface Adapters shell. This includes any auto-generated, schema-aware Object Relational Mapper (ORM) data access code.
Put into an architectural diagram, we end up with one layer of Interface Adapters:
Today has been a decent start to designing a slice of data access for a system designed with Clean Architecture. However, what if we wanted to optimise the system and include caching of data in, say, a Redis instance? How would this change our design? We’ll find out next time.
Today we will take a closer look at the Interface Adapter shell of Uncle Bob’s Clean Architecture.
In my first article on Clean Architecture, we learned about Business Logic but not much about Interface Adapters. Let’s do a quick recap on Business Logic and then dive into Interface Adapters.
Business Logic lives at the centre of our Clean Architecture universe. Everything inside the thin red circle is Business Logic—Entities and Use Cases. It’s what the system does, not how it does it—policy, not mechanism.
Programming the Business Logic is usually straightforward and should be even a bit boring. It simply deals with the aspects of business workflow that are entirely devoid of mechanism—i.e. no web, mobile, database, cache, external services, email, hardware, etc.. In our world of Clean Architecture and as specified by the Dependency Rule, Business Logic is entirely unaware of these things. It knows about them only through interfaces and other abstractions.
Business Logic communicates with the rest of the system via abstractions (i.e. interfaces) represented by the thin red line. These interfaces belong to the Business Logic. It’s these interfaces that make the system pluggable.
Beyond the red line live the Interface Adapters—here things get more interesting. This ‘shell’ connects the Business Logic to the mechanisms. It’s a conduit or channel layer that calls must pass through to make it to the mechanisms: web, database, external services, etc..
Below is a different depiction of the Interface Adapter shell. The adaptersare the Interface Adapters from the Clean Architecture diagram.
All incoming calls from Business Logic get converted into a format that the mechanisms can understand.
For example, our Business Logic makes a call—against an interface—to save a Customer. Say, we have a SQL Server database implementation—an adapter—for that interface. When the adapter receives the call to update a Customer, it will convert the Customer model to a CustomerDb model suitable for persisting to the Customers table in our SQL Server database. This adapter will convert the incoming call into one or more appropriate calls to the database. If needed, it will open a database transaction too. And the same thing will happen when the call returns— another conversion will occur, but this time from SQL Server models back to business logic models.
In the Clean Architecture diagram, the Interface Adapters are depicted as a single schematic shell yet may represent multiple shells. Some systems might be fine with a single shell, while other, more sophisticated systems may require further layers. Reasons for multiple shells might be
Consolidation of data from various data sources,
Persisting data to multiple data sinks,
Cascading data access (e.g. caching)
That’s it for today.
We will discover more about Interface Adapters next time.
With the traditional n-tier architecture model, the flow of control through the various tiers is as expected: First, it hits the top-most and user-facing Presentation Tier, then moves onto the business logic and finally calls into the Data Tier. From here, it makes its way back up through all the tiers to the external caller.
It’s less clear, though, how a call would traverse the concentric ‘shells’ of Clean Architecture.
Or is it?
A call coming into the system would traverse to the innermost shell, Entities and then back out again to retrieve data from external systems, say, a database. As the database responds, the call would bounce back along the same path that it came in on. The following diagram outlines (the blue arrows) the execution path taken through a Clean Architecture system.
An external caller, an Actor in UML, initiates the call, i.e. from outside the outermost shell.
Let’s play through an external request coming into an HTTP API:
The web server receives and accepts the HTTP request. Say, our web server is running ASP.NET WebApi. We are in the outermost shell of the Clean Architecture. The request moves through the ASP.NET pipeline, which will route it to the correct controller action.
The request has ended up as a call to a controller action method. Here, data that must cross over into the business logic shell will be converted to neutral business logic models. Then the flow of control is further delegated to a business logic workflow or use case.
We’ve crossed over into business logic to the Use Case shell. Here a business logic workflow is executed. This workflow will engage the help of Entities, the innermost shell, to fulfil its step-by-step instructions.
Suppose the use case must retrieve or alter data from a peripheral system like a database. In that case, it will make a call into the Interface Adapter shell through an interface or other abstraction. The call is moving back into the outer shells. Here we have data access logic that is aware of the specific business contect (e.g. we want to retrieve customer order data) as well as database specifics (e.g. SQL Server).
The call continues into the outermost layer, here representing the generic code suitable for all SQL Server databases.
The request proceeds outside of our Clean Architecture system and retrieves the data from our SQL Server database.
After all this, the flow of execution returns to our system and winds its way back up the same way until the data is returned to the external client, say, via an HTTP response.
The Two Synchronised Arrays Problem
/by Olaf ThielkeThe Two Synchronised Arrays Problem
Imagine we have a system for managing users. Say, within the system source code, two separate arrays hold information on users. One array contains unique identifiers for users, represented by integer ids:
user ids:
[ 1, 7, 9, 4, 12 ]
The second array contains the names of the users:
user names:
[ "Dave", "Carol", "Alice", "Eddie", "Bruce" ]
The elements at a given index correspond to the same user’s information in both arrays. Therefore, user “Alice” at (zero-based) index 2 in the names array has a user id of 9, also at index 2, in the user ids array.
The order of the elements in both arrays must be synchronised. If we remove the user with id = 9 at position 2 within the user ids array, we must remove it from the user names array also at position 2. The resulting arrays would see id = 9 and name = “Alice” spliced from both arrays at position 2:
user ids:
[ 1, 7, 4, 12 ]
user names:
[ "Dave", "Carol", "Eddie", "Bruce" ]
Can you see the problems we might run into with this flawed design?
What will happen when another programmer, unaware of the implicit relationship between the arrays, decides to reorder the user names in alphabetical order? Maybe they want to display the user names in alphabetic order in the UI:
reordered user names:
[ "Alice", "Bruce", "Carol", "Dave", "Eddie" ]
Yet the user ids array remains unchanged (!):
user ids:
[ 1, 7, 9, 4, 12 ]
Now, when we remove id = 9 at index 2, “Carol”—rather than “Alice”—will be deleted from the user names array. Oops!
Not only are we removing the wrong user name (“Carol”), but we are also retaining the user name we should have removed (“Alice”).
Why does this problem come about?
Even though the intent for these two arrays is to keep the ordering of the elements synchronised, there exists no mechanism to ensure this.
OK, how do we fix this?
A better way is to house related user ids and names together in an object:
users:
Why is this better? By combining the related user information into objects removes the need for order synchronisation between two arrays. If we want to delete the user with id = 9, we do so, and the entire user object disappears:
users with { “id”: 9, “name”: “Alice” } in position 2 removed:
And if we want to reorder the user objects by name alphabetically, then the user ids get reordered too:
users ordered by name:
Is there a general rule or principle to help us avoid such problems?
Yes, there is: The Single Responsibility Principle (SRP) from The SOLID Principles:
“Gather together those things that change for the same reason, and separate those things that change for different reasons.”
When it comes to the SRP, many of us—myself included—primarily consider the things we should separate—like UI & database. Yet, it’s just as important, if not more so, to place together functions and data structures that change at the same time and for the same reasons—like our user ids and user names.
Always Separate Stable And Unstable Code
/by Olaf ThielkeAlways Separate Stable And Unstable Code
Photo by Robert Gourley on Unsplash
Recently we discovered a general truth – stable things should not depend on volatile things. If they do, then they too will become unstable by association.
Why does this matter in programming?
Say we are working with a messy legacy software system where high-level business logic is intermingled with data access code. The business logic hardly ever changes—it is stable. Conversely, the data access code is modified often to reflect changes to the database schema—it’s volatile. Since data access and business logic are united, business logic code is also affected whenever we modify data access logic. Business logic has become volatile by being closely associated with the unstable data access code.
We have a system where we must make more changes than we would like. And fewer changes are better.
OK, what might be a better system configuration?
It would be one where we affect fewer lines of code whenever we make a change.
Let’s consider two different 1,000 line systems:
System 1 is the one we have been considering all along. Business logic and data access logic are inseparably joined. Let’s assume changes always affect 10% of code in this system. Also, 80% of changes are to volatile data access code and 20% to stable business logic.
Under these conditions, how many lines of code do we modify for the average requirements changes? (For the sake of simplicity, I am assuming that we are adding no new code):
Lines changed: 1,000 lines * 10% = 100 lines.
In System 1, the average code change is 100 lines.
In another system, System 2, Business logic is separated from data access logic. There exists a one-way dependency from data access logic onto business logic. So, data access logic could be affected when we alter business logic, yet business logic is isolated from data access logic changes.
Data access logic modification: 500 lines * 10% = 50 lines
Business logic change: 1,000 lines * 10% = 100 lines
(Simplifying Assumption: Business logic change always affects data access logic)
System 2 average number of lines changed: 20% * 100 lines + 80% * 50 lines = 60 lines
In System 2, the average code change is 60 lines.
In this simplified model, System 2 experiences only 60 lines changed on average, while System 1 is much more volatile with 100 lines of code changing.
We have just derived the rationale for the Dependency Inversion Principle.
Name Converters After The Output Type
/by Olaf ThielkeName Converters After The Output Type
Photo by Designnn.co on Unsplash
The question came up again. What shall we call a type converting from a source type to a destination type? Say, from an
Order
to aDbOrder
? How aboutOrderToDbOrderConverter
?Wouldn’t that mean that other source inputs producing the same destination object,
DbOrder
, required another converter class? SaySalesInvoiceAndPurchaseStatusToDbOrderConverter
? That’s a rather long name.With conversions, we are primarily interested in the resultant type—here
DbOrder
. That’s the type we need to carry on with our program.So, I suggest that we name converters after the output type. If we are converting to
DbOrder
then the converter type would shorten toDbOrderConverter
. For ease of use, all the methods become overloads ofConvert()
orMap()
. The source conversion types become the input parameters.For example:
The only caveat with this approach is that we carefully construct converters to be aware of only one mechanism at a time. For example, if a converter knows how to convert both an
ApiOrder
, a web type, and aDbOrder
, a database persistence type, to anOrder
, a business logic type, then this is a problem.Why?
Consider this: Where would such a converter type be located? Within the database project? Now the database project would have to know about web! What about the web project? Not any better—the web project would need to reference and become coupled to the database project.
What is required here would be two separate converters, each serving one adjacent transition between architectural layers.
Function Names Start With A Verb
/by Olaf ThielkeFunction Names Start With A Verb
Photo by Headway on Unsplash
Today’s Tip is foundational to good programming.
Functions describe system behaviour. They are actions. And as such, a function’s name should describe the work it performs.
What do these functions do?
InvertedImage()
Transaction()
Processed()
TotalAmountWithTax()
These function names are a bit ambiguous.
On the other hand, these function names are more descriptive:
ProcessExpenses()
ReconcileTranactions()
GetCustomer()
BuildVehicle()
The starting verb makes all the difference. The noun is also helpful—it describes the object of the behaviour; what the action is affecting. The noun is not always necessary. Sometimes it’s obvious. A
File
class with anOpen()
method will be about opening the file.The verb ought to be in the present tense. Simple Present Tense, e.g.
CreateGame()
, is more suited for naming functions than Present Continuous Tense, e.g.CreatingGame()
.Are there exceptions to the rule of putting the verb first?
Yes, there are.
Constructors: Constructors are functions setting up the initial state of an object. In many languages, the constructor name must be the name of the class:
Properties: In C#, we can have property getter and setter functions. We call these without parentheses, and they resemble data more than behaviour:
var totalAmount = ShoppingCart.GetTotal;
or
var totalAmount = ShoppingCart.Total;
The second option looks more natural. The first line confuses without invoking parentheses.
That’s it for today.
Please start function names with a verb.
Clean Architecture Is Wrong
/by Olaf ThielkeClean Architecture Is Wrong
Well, not hugely wrong. But still not entirely correct as I see it. This inconsistency has been bothering me for a little while. It’s not a big deal but something to be aware of.
And this flaw does not take away from the utility of Clean Architecture as a software engineering philosophy in any substantive way. I still firmly believe that Clean Architecture—for all its abstractness and potential flaw—leads to beautifully simple application designs that can be easily modified and extended. What’s not to love?
All right, let’s get into it.
Here is the familiar schematic diagram for Clean Architecture.
Clean Architecture
The Dependency Rule states that inner circles cannot know about outer ones. On the other hand, outer shells must depend on inner ones. In the end, some circles must know about one another, or it’s not a productive program but merely chunks of unconnected code.
So, outer rings know about inner ones, and the little arrows depict this one-way relationship:
Use Cases know about Entities—Entities are unaware of Use Cases.
Interface Adapters know about Business Logic—Business Logic is ignorant of Interface Adapters.
Frameworks & Devices are aware of Interface Adapters, yet Interface Adapters are oblivious to Frameworks & Devices.
I have a problem with the last statement. I believe it to be wrong.
Why?
Let’s assume the statement is correct and that Frameworks really are dependent on Interface Adapters.
Say, in a system, we use SQL Server as our database technology.
It would mean that our generic SQL Server data access SDK should reference, or depend on, our specific data access code to retrieve data from our SQL Server database!
Inversely, our specific SQL Server data access could not call generic SQL Server data access functions to fetch or save data since there exists no reference! How is our application-specific data access mean to do its job?
In my opinion, the dependency should be the other way around between the outermost layers.
Interface Adapters depend on Frameworks & Drivers (red arrow).
The Interface Adapters shell connects interfaces exposed by Business Logic with those of the general code of Frameworks, libraries and SDKs.
Adapters act as connectors between two dissimilar interfaces. To see as much, take a look at that adapter plug you’re using to connect the toaster/TV/microwave you bought in the UK to your power outlet. It has both a male and female plug interface. The toaster does not need to know about the UK adapter, and neither does your power outlet—neither depends on the adapter. On the other hand, the power adapter depends on both UK and local power outlet interfaces.
Photo by Call Me Fred on Unsplash
Adapters always depend on both interfaces. Yet, the things that an adapter connects do not, by themselves, depend on the adapter.
Adapters depend on both interfaces.
Therefore, it stands to reason that within the context of Clean Architecture, Interface Adapters depend on Business Logic interfaces and the outermost Frameworks circle.
That’s it. I hope that makes sense.
What do you think? Am I missing something? Feel free to comment or send your opinion to olaf@codecoach.co.nz. I want to learn and see where I am going wrong! :-)
Clean Architecture – Caching As A Proxy
/by Olaf ThielkeClean Architecture – Caching As A Proxy
In the last post on Clean Architecture, we explored how caching could be implemented in a pluggable manner within the Interface Adapters layer.
Here is the diagram of the data retrieval part of the system:
I have highlighted the interface connecting from
CachedCustomerRepository
to the database. However, there is a problem.Again here is the listing for
CachedCustomerRepository
:Notice how the interface to the database is named
ICustomerDatabase
.As I see it, we can do improve this design.
As it stands, if we wanted to revert to a simpler system, one without the caching infrastructure and rip out
CachedCustomerRepository
, so that we can connect up theSqlCustomerRepository
to the use case, then we would be in trouble. The use case usesICustomerRepository
, yet SqlCustomerRepository implementsICustomerDatabase
!To make this work, could we have our business logic make its data calls against
ICustomerDatabase
?We don’t want the business logic to be aware it’s calling a database—the use case should utilise a neutral interface like
ICustomerRepository
.How about
SqlCustomerRepository
implementingICustomerRepository
instead ofICustomerDatabase
?Let’s try that out. Here is what the system diagram will look like without caching:
Yes, the system is pluggable—we can unplug
SqlCustomerRepository
and plug in a different database implementation.OK, what about if we reinstate caching? Here is the system diagram:
Isn’t that nice?! The entire caching infrastructure, especially the logic module switching between cache and database data retrieval,
CachedCustomerRepository
, plugged upstream into the use case and downstream into theSqlCustomerRepository
modules. It just connects up!Technically, the new implementation of
CachedCustomerRepository
is a proxy, a segment that sits between two interfaces of the same type. Proxy is a valuable design pattern—it allows us to slip behaviour between an interface consumer and interface implementer.Our new versatile system, whereby database implementation, caching implementation and the data retrieval workflow are pluggable modules, resembles how Lego blocks connect to one another. Elegant Software Architecture is highly pluggable—in the right way.
Clean Architecture – Caching With Interface Adapters
/by Olaf ThielkeClean Architecture – Caching With Interface Adapters
Last time we discovered the versatility of Clean Architecture’s Interface Adapters shell and how it acts as a connecting layer between the central Business Logic and our system’s specific technologies—the Frameworks & Devices.
In the example, we had business logic that wrote to and read from a SQL Server database. The code that lets us save and retrieve data from the specific database schema belongs in the Interface Adapters. Let’s check out the design we ended up with last time:
The advantage of this design is that when the database schema changes, our SQL Customer Repository Adapter will reflect those changes yet leave our Business Logic unaffected. Here we have powerful pluggability.
OK, let’s make things more interesting.
We want to introduce an optimisation—data caching. Instead of every data read running off to the database, we want first to check whether the data exists in a cache. If it does, return the data and do not read from the database. If not, go to the database, read the data, and put it in the cache for subsequent reads.
Now, where should the logic reading either from the cache or the database live? What about the entirely separate logic connecting us to a specific Redic cache implementation?
Do these belong to the business logic?
No, our business logic is the wrong place. These are both data concerns. All the business logic is concerned with is a way to read this data—that’s it. How the data is retrieved is not its concern.
OK, does this logic belong to the general SQL Server and Redis caching code? i.e. in the Frameworks & Devices shell?
No, that would also be incorrect. We don’t want to mix specific and generic data access.
On the other hand, it makes sense to have the code connecting our specific data retrieval from Redis cache (i.e. construction of cache keys, etc.) in a module at the same level as our specific SQL Server data retrieval code, SQL Customer Repository Adapter.
Furthermore, the logic switching between cache and database reads must sit in front of, and connect to, both the cache and database modules.
OK, so a picture is forming as to a system design that includes caching:
We now have two layers of logic in Interface Adapters—firstly, a module to switch between reading data from cache or database. Secondly, the adapters to retrieve data from Redis and SQL Server.
The logic to switch between cache and database is abstract. It does not mention Redis or SQL Server as the given cache or database technologies. Why? We get the flexibility to plug in other caching and database technologies.
Here is an example implementation of a class managing the Customer data retrieval logic, first from a cache and then from a database:
What we have here is a decent pluggable design. However, we can improve on it further by making a small change. We’ll look into that next time.
Clean Architecture – Interface Adapters Example
/by Olaf ThielkeClean Architecture – Interface Adapters Example
Today I would like to look at a simple Clean Architecture example to see what part the Interface Adapters shell plays in data access.
I can’t wait until next time when we will increase the capability of our Interface Adapters layer by introducing data caching.
OK, let’s get into it.
Say we have a system that we have designed with Clean Architecture guidelines in mind.
There is some business logic, a use case class, that needs access to customer data, and it gets this via an
ICustomerRepository
interface:Note: For the sake of simplicity, all the calls are synchronous.
Customer data is stored in a SQL Server database table called Customers. We have generic SQL Server framework code to save and retrieve data from SQL Server databases in general. This generalised framework code belongs in the outermost Frameworks & Devices shell of our Clean Architecture diagram.
OK, where do we put the connecting code, the behaviour, that utilises the generalised SQL Server framework but is about our specific database and how we store and retrieve customer data from it? Behaviour like reading the database connection string from the application-specific configuration; reading data from the Customers table, and possibly some other joined tables.
Maybe such functionality belongs to the Business Logic?
Business Logic is meant to be ignorant of data mechanisms like SQL Server. Referencing SQL data access code aware of our database type and internal schema suggests intimately knowing data mechanisms. No, we can’t put this code into the Business Logic layer.
What about the Frameworks & Devices shell? Well, it doesn’t here either. This outermost layer schematically holds generalised frameworks and is, in the context of SQL Server, concerned with generic SQL data access.
Our SQL Server database-specific code belongs to the Interface Adapters shell. This includes any auto-generated, schema-aware Object Relational Mapper (ORM) data access code.
Put into an architectural diagram, we end up with one layer of Interface Adapters:
Today has been a decent start to designing a slice of data access for a system designed with Clean Architecture. However, what if we wanted to optimise the system and include caching of data in, say, a Redis instance? How would this change our design? We’ll find out next time.
Clean Architecture – Interface Adapters
/by Olaf ThielkeClean Architecture – Interface Adapters
Today we will take a closer look at the Interface Adapter shell of Uncle Bob’s Clean Architecture.
In my first article on Clean Architecture, we learned about Business Logic but not much about Interface Adapters. Let’s do a quick recap on Business Logic and then dive into Interface Adapters.
Business Logic lives at the centre of our Clean Architecture universe. Everything inside the thin red circle is Business Logic—Entities and Use Cases. It’s what the system does, not how it does it—policy, not mechanism.
Programming the Business Logic is usually straightforward and should be even a bit boring. It simply deals with the aspects of business workflow that are entirely devoid of mechanism—i.e. no web, mobile, database, cache, external services, email, hardware, etc.. In our world of Clean Architecture and as specified by the Dependency Rule, Business Logic is entirely unaware of these things. It knows about them only through interfaces and other abstractions.
Business Logic communicates with the rest of the system via abstractions (i.e. interfaces) represented by the thin red line. These interfaces belong to the Business Logic. It’s these interfaces that make the system pluggable.
Beyond the red line live the Interface Adapters—here things get more interesting. This ‘shell’ connects the Business Logic to the mechanisms. It’s a conduit or channel layer that calls must pass through to make it to the mechanisms: web, database, external services, etc..
Below is a different depiction of the Interface Adapter shell. The adapters are the Interface Adapters from the Clean Architecture diagram.
All incoming calls from Business Logic get converted into a format that the mechanisms can understand.
For example, our Business Logic makes a call—against an interface—to save a Customer. Say, we have a SQL Server database implementation—an adapter—for that interface. When the adapter receives the call to update a Customer, it will convert the Customer model to a CustomerDb model suitable for persisting to the Customers table in our SQL Server database. This adapter will convert the incoming call into one or more appropriate calls to the database. If needed, it will open a database transaction too. And the same thing will happen when the call returns— another conversion will occur, but this time from SQL Server models back to business logic models.
In the Clean Architecture diagram, the Interface Adapters are depicted as a single schematic shell yet may represent multiple shells. Some systems might be fine with a single shell, while other, more sophisticated systems may require further layers. Reasons for multiple shells might be
That’s it for today.
We will discover more about Interface Adapters next time.
Clean Architecture – Follow A Request
/by Olaf ThielkeClean Architecture – Follow A Request
How does a request or call proceed through the Clean Architecture?
With the traditional n-tier architecture model, the flow of control through the various tiers is as expected: First, it hits the top-most and user-facing Presentation Tier, then moves onto the business logic and finally calls into the Data Tier. From here, it makes its way back up through all the tiers to the external caller.
It’s less clear, though, how a call would traverse the concentric ‘shells’ of Clean Architecture.
Or is it?
A call coming into the system would traverse to the innermost shell, Entities and then back out again to retrieve data from external systems, say, a database. As the database responds, the call would bounce back along the same path that it came in on. The following diagram outlines (the blue arrows) the execution path taken through a Clean Architecture system.
An external caller, an Actor in UML, initiates the call, i.e. from outside the outermost shell.
Let’s play through an external request coming into an HTTP API: