Data independence is key to robust architecture and non-fragile unit tests

I have an 8-year old girl who rides the school bus to go to her school every morning. So we’ve waited for the school bus together on the sidewalk. But some day I drove behind a school bus and had to stop when the bus stopped to allow the kids cross the street safely.

With nothing to do, I looked at the school bus and noticed something. It has a different vehicle number than the route number. My girl remembers her route number on the very first day of school. But we never knew there’s also a vehicle number.

And there’s a reason for that, and a lesson to learn. The vehicle number is for the mechanics, whereas the route number is for the students. The same bus is presented to the mechanics and the students differently, so that the information is useful, and not a hindrance.

We can apply this simple HIG from the real world to our code.

The school bus example

A school bus has two numbers.

Route number – MW01, MW02, MW03
– More prominent. Large font size. In front and on the right side of the bus. So riders can clearly see.
– ‘MW’ is an abbreviation for the name of the school – Mosby Woods Elementary School.
– The digits are the route numbers, prepended by a 0 if necessary to make sorting and alignment easier.
– For a given school bus, riders can easily tell which school the bus is going to AND which route the bus takes.
– ‘MW’ is intuitive for all students at Mosby Woods. A student only needs to remember the route number – 1, 2, 3, … – in order to get back home after school.
– Even if there’s another school named Merry World High School, nobody is going to be confused because it’s likely far away enough for the riders to wait at the same stop. And you can always add more letters to the abbreviations such as MWES and MWHS.

Vehicle number – 7982, 5604, 1093
– Smaller font size. On the left side of the bus. Hidden from riders.
– The maintenance crew can easily and uniquely identify each bus.
– The mechanics working on a bus don’t have to know which school it goes to because it’s irrelevant.
– They also don’t need to know the route it takes.
– They can use the vehicle number to track the complete history of problems of a bus, and write up a report after an inspection.

The bottom line: For the riders, MW01 is easier to remember. For the mechanics, 7982 is easier to track. As a result of this different data presentation, they can do their things independently of each other.

In terms of code

Given the same bus object, the model to represent it can differ depending on your purpose.

Assume we’re building a school bus route planning app. The user is an employee of the school board who is in charge of assigning school buses for every school in the district. He has to take into consideration if the following:

  • Maintenance conditions
  • School locations and student household addresses

Based on these information, he has a difficult job of assigning the appropriate buses for all the schools so that all students can get to their schools safely and on time.

Let’s say the app has a ListBuses scene which lists the available buses for route planning purpose. A bus is available only if it passes all maintenance checks to be deemed safe. This means the ListBusesInteractor needs to run inspection checks on every bus history. The RunCheck use case needs to operate on bus objects by their vehicle numbers. A Bus entity model may look like:

By the time the Bus object travels through the VIP cycle and arrives at the view controller for the user to plan routes, the vehicle number is irrelevant. All the buses listed already pass maintenance checks and are deemed safe to carry students. The route number is a lot more useful to him as he can deduce the school name and the route from a route number. This means the ListBusesPresenter needs to covert Bus objects to a more convenient view model suitable for display to the user. A DisplayedBus view model may look like:

Less useless information is clearer.

Making isolated changes

Let’s say school regulations tighten up and require more stringent maintenance checks due to recent accidents. We need to add the name of the mechanic who approves the inspection check so that we can get a paper trail when an accident happen. The new Bus entity model may look like:

Our runCheck() method will certainly need to change. We’ll need to access the new inspectorName variable. Maybe an extra guard statement before returning true.

That is the only place we need to change to accommodate the new regulation!

The rest of the ListBuses scene can stay intact and will continue to work as expected, because, beyond the presenter, our app operates on the DisplayedBus, not Bus objects.

If the presenter didn’t convert from DisplayedBus to Bus, the Bus class will permeate throughout the entire app. This’ll require you to make many changes in a lot of places. It’ll also break many unit tests, making them very fragile.

Data independence is extremely important in writing maintainable software and unit tests. A little foresight may seem boilerplate and overkill now. But it’ll save you from having to repay huge architectural debt later.

Get the Clean Swift Xcode Templates

Join over 15,000 developers and subscribe to get my Xcode templates. Learn how to apply the VIP cycle to your projects. Extract business and presentation logic into interactor and presenter. Route to other scenes and pass data. Write fast, maintainable unit tests. Be confident making changes.

I promise I'll never send you spam. You can unsubscribe at any time.

Raymond
I've been developing in iOS since the iPhone debuted, jumped on Swift when it was announced. Writing well-tested apps with a clean architecture has been my goal.

1 Comment

  1. Clean Swift and Realm…

    How would you handle this? Especially the Realm “Results” in a paging list…
    As the “Results” ist lag loading and does not have a limit or offset.

    I think, we should get the data from db in the Interactor and prepare it for display in the Presenter. But when we come back for page 2 to the Interactor, there is no chance to get the data for page two…

    Do you have any suggestion?

Leave a Comment

Your email address will not be published. Required fields are marked *