- Apr 10, 2025
The Humble Object Pattern
The idea is simple: Split the behaviors into two modules. The first module is humble; it contains all the hard to test behaviors. The second module contains the testable behaviors.
For example, the user interface module is humble. Why? Because it's very difficult to unit test that the appropriate elements are correctly displayed on screen. However, the behavior of that user interface can be unit tested.
As such, the database layer is humble. It simply uses SQL to access the data required by each data access method. The business logic code, in contrast, is not humble because it encapsulate application-specific business rules.
Let's begin with the user interface pattern.
The User Interface: Presenters And Views
The View is the humble object that is hard to test. It's code is kept as simple as possible. Simply moving data into the UI. The Presenter object is not humble. It accepts data from the application and prepare it for presentation so that the humble View simply move that data presentation to the screen.
For example, if the application wants to display a date in a UI element, it hands the Presenter a Date object. The Presenter formats that Date object into a string. It then place that string into a simple data structure we call the View Model.
Everything that appears on the screen, and that the application has control over is represented in the View Model as a string. Nothing is left for the View other than displaying View Model data into the screen. Thus, the View is humble.
Here is a diagram showing the MVP pattern.
The Database Layer: Data Gateways And Data Mappers
The database gateway resides between the business logic and the database. The gateway is an interface with CRUD methods executed by the application on the database.
For example, if the application needs to fetch the list of users who logged in on a specific day, then the UserGateway interface will have a method named GetUsersWhoLoggedInAfter that takes a Date object as input and returns the list of users.
Looking at the above diagram, which layer do you think contains the ORMs code like Nhibernate, EF or Hibernate? The database layer of course. Indeed, the ORMs / Inline SQL code resides in the UserDataMapper module.
Remember, we don't allow SQL in the business layer, we use gateway interfaces with appropriate methods instead. It's the responsibility of the database layer to implement those gateway interfaces. The user data mapper implementation is the humble object. It represents the Humble boundary between the gateway interfaces and the database.
The business logic however is not humble because they encapsulate application-specific business rules. These are the testable ones, because the gateways are replaced with stubs in test code.
Final Thoughts
It has long been known that testability is a good architecture attribute. The separation we've done above, between testable and non-testable modules defines an architectural boundary. The first example is the Presenter/View boundary. The second example is the Data Mapper boundary.
The Humble Object design pattern is often underrated. It's this pattern that enables testability thus a better software architecture.
Related Materials
Get exclusive deep dives, private notes, behind-the-scenes thinking, and raw experience from the field. Exclusive insights from the mind of a pragmatic architect.