Software Design Thinking

Karthikkeyan Bala Sundaram
7 min readAug 27, 2018

In this article, Im gonna share the approach that I took to solve a complex problem by composing individually working classes.

Warning: This is a lengthy article to read.

I been working in an iOS project for almost an year. It is almost production ready. The architecture of the project was finalized way back. We built each layers, from Network Layer to Presentation Layer, and the communication between each layers well defined. Below is the high level architecture of the project,

Base Architecture

Please refer iOS App Architecture for the detailed explanation of the above architecture.

New Requirement

Right when we thought every class and layer are working perfectly, there comes an another requirement.

In a screen, if/when an API fails because of timeout error,

  • Cancel all the existing network calls on that screen
  • Display an alert with a generic message and asking the user to Retry
  • Upon user’s decision to Retry, call all the API related to that screen and refresh the data in the screen.
  • Importantly, the logic/component should be generic or flexible enough, so that it can be used through out the project.

This seems simple to implement, but after a year it became complex to mess around with the underlying network layers. So I decided not to directly make code changes in the existing classes.

Approaches

I have tried two approaches before finalizing one,

Notifications

NSNotificationCenter is the first thing that popped out of my head and it was encouraged by fellow engineers. The approach is to post a notification when ANY request timeout.

Notification Approach

This looks simple, but there are couple of fundamental issues with this approach,

  1. Any objects can register for this notification, even the classes those are not relevant to the API can register to get notified. I wanted to set the boundaries of each classes.
  2. One of the requirement is to cancel all the network calls triggered from the screen. With this approach there is no way for the view controller to know which network calls are currently running, so it cannot cancel them.
  3. Even if the view controller some how managed to obtain that information, view controllers are not allowed to interface with network layers. If it does then it a violating in my architecture.

Given the above, notification approach doesn’t seems feasible, so I have to look for other approaches.

One notable takeaway here is, Observer Pattern(NotificationCenter-Notification), pattern by passes all the middle layers in between network and presentation layers. All the middle layers code remain unchanged.

View Models

Since the view models are the data-backing layer for view controllers in the architecture, the second approach I was thinking was to handle the timeout error of each API calls in view models.

  • When API fails/succeeded the network operation is gonna call the completion block in view model.
  • If the error code is of timeout, then the view model will notify view controller to present retry alert message to the user.
  • View model cancels all the network calls it initiated.

Something like this,

View Model Approach

This approach seems pretty simple. This is not violating the architecture. All the layers are communicating with one layer above and one below. I could make it work. However, there are some major issues with this approach.

  • Lots of Code Change: Note that the above example is just for one API call. A view model could be working with multiple API calls, so I will have to update all the completion blocks of the view model. If I have to implement the retry logic in another view controller, I will have to update all the completion blocks in one more view models. We are talking about updating all the completion blocks in most of the view models.
  • Bugs: More modification in existing code, more prune to bugs.
  • Open/Close Design Principle: What if I want to handle one more error type in the future. I will have to update all the ViewModels once again. With this approach we are going against Open-Close Design principle.
  • This is a brute force approach.

What I need

  • I want to extend my existing code, but don’t want to modify.
  • I can leverage Observer Patterns.
  • I want to the new component completely modular.
  • With or without connecting the new component into the existing architecture, the existing code should work.
  • The new component should not be limited handling Timeout error.

Components

Lets dig deeper into implementation details. Lets list out what are all the classes required,

Response Proxy

First thing first, it all starts with an API response. I need a entity that checks any API response for error. If the response is error, then that entity should some how send that message to the presentation layer.

Most importantly, the network layer’s functionality shouldn’t be dependent on this new entity. Basically the new entity should be of optional type.

Since this entity is acting as an agent, that handles the response on behalf of Network Layer, we call that entity ResponseProxy.

ResponseProxy

We have our proxy class ready. Now the questions are,

  • Who creates & keep the proxy object alive.
  • Who sets the proxy object to network operations.

The answer is the whoever manages the network operation. In my case, I have an NetworkOperationQueue which lives as long as the app is in the memory. This is the right place for me to create the proxy object.

Operation Queue and Operation

Note that in DataTaskNetworkOperation the property responseProxy is optional. We are only letting the proxy to take over the response-handling if it presents. This give the network layer a flexibility to fallback on to the original implementation.

ErrorCenter

Next, I need an entity that posts notification when an error is received by ResponseProxy. An entity where any objects can register to get notified, when a Network Operation or a group of Network Operations fails.

ErrorCenter

Error Observable

Next thing I want is an error observer object. An objects that supplies essential information and provides an interface for ErrorCenter. Object that confirms to ErrorObservable can be registered to receive notifications when error received.

ErrorObservable

ErrorHandlable

The last piece I need is, an entity that can handle the error observed by ErrorObservable.

ErrorHandlable

Im only talking about the happy path with all the above code snippets. I haven’t included the complete implementation details.

I have all the entities I need to compose a error handling flow. This is how it looks after i connect the individual entities.

Error Handling Flow

To simple put,

  • The OperationResponseProxy say’s, “You got a response from API?, I can handle that.
  • Then the Response Proxy, check if the response is “error response”, if so then it handover the error to ErrorCenter
  • Then the ErrorCenter checks if anyone ErrorObservable registered to receive notification for that error and notify that observer.
  • Then the ErroObservable will ask ErrorHandlable to handle the error in the UI (Retry or Cancel or Update UI)

Putting it all together

Now it time for me to link the new “modular sub architecture” with my current architecture. And this is how it looks like at the end,

Complete Architecture with Error Handling

Note that the core app architecture(the one on the right) still maintains the same.

Software Design Thinking

Now lets come back to the title of this article, “Software Design Thinking”. How am I trying to relate this article with the title? I will answer that question in three points.

Program to an Interface, not an Implementation

If you notice almost all the entities, those are connecting my core architecture, are Protocols aka Interfaces. The concrete classes that conforms to the protocols. Because of this nature, we get two benifets,

  • It gives the classes, the ability to dynamically change the implementation at runtime.
  • Classes are loosely coupled
  • Good use of Polymorphic :)

Example, the Network Layer exposes an interface ResponseProxy doesn’t care about the implementation.

Favor Composition Over Inheritance

In my example above, all the screens, that handles timeout error, needs to display an alert message with Cancel & Retry action buttons. I didn’t create a UIViewController that presents an alert. Instead I composed the class with ErrorObservable and ErrorHandlable. Doing so, gave the implementer a flexibility to create the observer it wants and handle the Retry & Cancel logic.

Embrace Design Patterns

Design patterns are time-proven designs to provide a solution to a software design challenges. In my design, I didn’t try to implement my own patterns. When I saw this challenge we went on a search to find what are all the existing patterns that fits my problem. I found Proxy Pattern and Observer Pattern that would work better, so I used it.

I don’t always keep all the design patterns in my finger tip to program. When I see a problem space, I try to find a pattern that already solves the problem. This way I don’t to stress myself to reinvent a wheel.

Note: I haven’t talked about all the implementation details and challenges that I encountered. That not what I this article about. In this article, I mainly wanna share how I think when I approach a problem than the implementation challenges.

Hopefully this helps you in your projects. If you have any questions or you have a better solution than mine, please leave a comment.

Thanks for reading.

--

--