Monday, January 11, 2016

How to Structure an Angular 1 application


Here is a post I wrote quite a while back. Didn't really finish my thoughts completely, but I am going to post it anyway. Since I now work on an application that is built very differently than this and uses a HP custom framework based on Knockout I probably won't revisit this. Also, Angular 2 dependency injection solves some of the problems I was dealing with below.



Okay, so the above  is a crude diagram of how we currently structure our Angular application. This approach is based on the idea that controllers should be thin and stateless and that your server communication layer should also be thin and stateless and resilient to server side changes. Part of this architectural principle stems from the fact that our server team is a different team so we want to maintain flexibility in adapting to them without impacting our code.

We have the "View". We see this as html and directives. Then we have our "Controllers" which are more accurately "Thin Presenters". Because the controller lifespan is very much related to the view our goal has been to primarily use these just for binding. They are likely to be stateless although they may contain state which is heavily dependent on the UI. For example you may have some sort of selection which exposes another part of the UI and this selection state may be in the controller.

The Controllers box also has helper controllers. This is to account for the fact that you may want to replicate certain code between Controllers. Helper controllers are created via "$controller" and do not have a lifespan outside of where they are created. An example might be paging information which you may want to do on many pages, but whose state should be very limited.

We have the "Persistence" layer. I call it that because architecturally it corresponds to the persistence layer in more traditional web development. It is a stateless layer that provides a Promise based API. The key functionality that it has is that it is responsible for all object transformations. We get and send some JSON to and from the server, but in our code we want to deal with our objects. Now hopefully in most cases these are very similar, but that is not always the case. This layer is meant to insulate us from server JSON changes. The actual transformations are in the model objects themselves who know how to construct and deconstruct themselves from and to server side JSON. We also have the notion of stateless helper services. These services help in the transformation when something is complex. For example, we have a selection tree that gets turned into a general set of rules so we have a independent service that provides this logic.

The middle part I skipped is labelled "Logic Model". This holds our application state and our business logic. It is the core of the application. It is largely ignorant of the views and how it will get displayed.

Now all of this is pretty straight forward. This is basically the same kind of layering people have been doing for a decade slightly shifted into Angular. The key aspect is the separation of state from controller by using Angular services. But this falls down is several ways.

Now the interesting part that is currently in my mind. The failure case. The parts where this architecture doesn't work.

"Page / UI Context" State Used by Multiple Controllers
You can't put this state in a controller because you want access in multiple controllers. But you don't want to put that state into a service because that makes the state live longer than it is supposed to. You want the state bound to the lifetime of the controllers. There are many solutions to this issue, the trick is to find one that makes sense and will not cause problems in the future.

  • Place this stuff in a high level controller and access it via scope
  • Use a Service, but call a "reset" function
  • Create an Object that can be Shared by multiple controllers
    • How does it get shared? You could access it via a key, but how do you share the key
      • State Key could go in URL
      • State Key could go in Scope
I don't like placing stuff in scope and relying on it being there in a sub controller. It introduces a very hard to track coupling between controllers. I also don't like having a Service with a reset since this can easily have problems later if reset is not called or updated properly.

It seems like the right pattern is to have a Service that returns a object based on a contextual key. The Service acts as both a factory and a manager for these objects. The question then becomes, how do you manage the key. It must be shared between controllers. If you have something like an id in the URL you are set, but what happens when you don't. Although I dislike inheriting stuff from scope I think it is the construct the makes the most sense. 



No comments:

Post a Comment