Default Architecture for a UI Application

In this post I’m going to cover the default/general architectural structure that I use when building a UI application.

Application Architecture Composition

When designing the architecture of an application I like to break the package down into components. Each component is responsible for providing an interface to a set of cohesive features that can be loosely coupled with other components to provide the full set of features required by the application.

As a general rule and default starting place for a GUI application, the primary components will be an implementation of the MVC architectural pattern. This decision encourages the notion that there should be at least three components in our application: Model, View and Controller. In order for these components to be loosely coupled, the application will likely need a few services such as a message broker and a dependency injection service. So I’ll add another component called Service. All applications need to have an entry point into the package, so I’ll add one more component to the solution that will be called UI as this is the component that is responsible for providing users access to the application.

Within the Model component, there are two subcomponents: Command and Event. The Model.Command component isolates data structures used to represent user commands sent from objects in the View to objects in the Controller. The Model.Event component isolates data structures used to represent events raised by objects in the Model changing state.

Below is a UML component diagram that shows the dependency chain of these components. I used UMLetino to create the diagram.



The Model and the Service components are the most important since all of the other components depend on them. Also notice that neither the Model nor the Service components depend on any thing.

Model Component

The Model component is responsible for providing the state of the application and encapsulating the logic the application is providing the user the ability to work with. It is also responsible for declaring Command and Event objects that are used to model the behavior the application is modeling. For example, if the application was supporting an Accounts Receivable Clerk’s ability to create invoices, then there would likely be an Account object in the Model component, a GenerateInvoice object in the Command component and an InvoiceGenerated object in the Event component.

Most applications will require some sort of user security where a user must authenticate their credentials to prove their identity to the system and once their identity is recognized, the system will grant them authorization to access and potentially manipulate resources controlled by the system.

If the application requires tracking the state of a user, they could be modeled in a Session object. An active Session would have properties such as an Identity representing the user’s information such as name, title, contact information, etc.; and a set of authorization Claims representing the authorizations granted the user. It would also provide a method to end the Session. The commands and events corresponding to the Session would include objects such as AuthenticateCredentials, AuthenticationFailed, SessionInitialized, SessionStarted, and SessionEnded.

View Component

The View component is responsible for providing a visual representation of the state of theĀ  objects in the Model component. Objects in the View component will use a message broker to subscribe to one or more Model.Event(s) in order to be notified when a Model object has changed its state.

Objects defined in the View component are responsible for translating user interactions into the appropriate Model.Command objects and publishing those commands via a message broker. Typically, applications display a nameplate/masthead that display the organization’s logo, controls for accessing the user’s profile, as well as the ability to logout, and a menu of links to resources/features of the application. These nameplates come in many styles and each system will require the application’s display stylistically match the organization’s branding. To design an application with this minimal feature, we would need to include Masthead, Logo, Profile, and IdentityChallenge objects in the View component.

The Masthead would subscribe to the SessionInitialized event and would handle receiving notification of that event by checking if the Session that was initialized is currently active and if it is, render the Logo, Profile and itself. If the Session isn’t active, then the Masthead would remove the Profile from view and potentially even change the style of the Logo to be larger and/or in a different position.

Similarly, the IdentityChallenge would be subscribed to the SessionInitialized event and would check if the initialized Session was active. If it is active, the IdentityChallenge would remove itself from the UI as it is not required. If it isn’t active, it would append itself to the UI in order to allow the user to authenticate/start a new Session.

Controller Component

The Controller component is responsible for controlling the state of objects in the Model. Objects in the Controller component will use a message broker to subscribe to one or more Model.Command(s) in order to be notified when a View object has been interacted with by a user. Objects defined in the Controller component are responsible for calling the appropriate methods on a Model object based on the received Command and publishing the results as a Model.Event object. In some cases, the Model object’s method that corresponds with the received Command will require data that is external to the application. In those cases, the Controller object will use a service to make a call to the external data source and use the results of that request to fulfill theĀ  Command.

As an example, if the Profile object in the View component contains a button for logging out, when the user clicks that button the Profile will publish a new EndSession command. The SessionController object will be subscribed to that event and be notified. It will then send an HTTP request to the identity and access management API (examples are Auth0 or Google’s Firebase Auth) to end the user’s session. When it receives a success response it will call the End() method on the Session object.

Service Component

The Service component is responsible for providing application services to the other components. These services include a dependency injection container and a pub/sub message broker.

Objects defined in the Service component are responsible for handling the infrastructure duties required by any application to function in its host environment.

UI Component

The UI component is responsible for providing the entry point of the application. In the case of a browser-based application, the host environment is a browser DOM’s window object. When the window loads, it will fire a load event. The UI component makes use of the EventTarget interface and adds an EventListener which will use the Service‘s dependency injection Container to initialize the application based on configuration settings. Once the application is initialized, it will publish a SessionInitialized event via the MessageBroker. View objects subscribed to the SessionInitialized event will render the appropriate user interface and enable the application to respond to the user’s interactions as described in the View Component section above.

Next Steps

With the basic architectural guidelines defined, when a project needs a user interface application, this architecture can be used to provide a launching pad for defining the product requirements that a developer will need to implement. The next post in this series will be about defining these requirements for a fictional system.