-
Notifications
You must be signed in to change notification settings - Fork 25
Implementation of a Service Oriented Architecture Façade
Given a complex subsystem like the UFS, it can be made easier to use by implementing a simple interface. To better handle the stages of such a complex system, rather than firmly wrapping the entire system, we can handle the various services through a series of drivers underneath this interface. This also allows us to access these drivers individually to support power users who may need more options than the interface might provide. To fulfill these intents, we propose a service-oriented architecture façade design pattern to provide a unified interface and to simplify the communication between components.
Currently, the apps within the Unified Forecast System (UFS) require direct user control of each run stage and specific settings of the configuration files. A summary of these stages for tier 1 supported machines is as below:
SRW | Global Workflow | HAFS |
---|---|---|
Run activate regional_workflow modify run run |
under /sorc, run Create necessary symlinks by running Set up initial conditions file run Usage: go to EXPDIR and ensure config.base variables are correct run workflow generator to create the ROCOTO configurations start run with set up crontab |
Build and install components with Edit config files: /parm/hafs_input.conf /parm/hafs.conf /parm/hafs_holdvars.conf /parm/hafs_basic.conf Start run with Usage: Mandatory arguments: 2014062400 -- the cycle to run 95E -- storm id case_root -- FORECAST = real-time mode, HISTORY = retrospective mod /path/to/parm -- location of parm directory where standard conf files reside |
Despite the variations, all three apps can be broken down into generic tasks:
- Build the app
- Define a configuration that includes the experiment parameters, its input files as well as its output
- Submit the HPC job
As these are similar between the apps in terms of user intent, they vary notably in required interaction, which is where we can see the benefits of the façade pattern. For the purposes of this façade, we will focus on the configuration step to maximize the benefit to end users.
Below is a pseudocode example of what the scaffolding of a façade implementation might look like, referenced from Refactoring Guru. Here, the sub-tasks involved in the configuration generic task above can be placed as the subsystem blocks below:
from __future__ import annotations
class Facade:
"""
The Facade class provides a simple interface to the complex logic of one or
several subsystems. The Facade delegates the client requests to the
appropriate objects within the subsystem. The Facade is also responsible for
managing their lifecycle. All of this shields the client from the undesired
complexity of the subsystem.
"""
def __init__(self, subsystem1: Subsystem1, subsystem2: Subsystem2) -> None:
"""
Depending on your application's needs, you can provide the Facade with
existing subsystem objects or force the Facade to create them on its
own.
"""
self._subsystem1 = subsystem1 or Subsystem1()
self._subsystem2 = subsystem2 or Subsystem2()
def operation(self) -> str:
"""
The Facade's methods are convenient shortcuts to the sophisticated
functionality of the subsystems. However, clients get only to a fraction
of a subsystem's capabilities.
"""
results = []
results.append("Facade initializes subsystems:")
results.append(self._subsystem1.operation1())
results.append(self._subsystem2.operation1())
results.append("Facade orders subsystems to perform the action:")
results.append(self._subsystem1.operation_n())
results.append(self._subsystem2.operation_z())
return "\n".join(results)
class Subsystem1:
"""
The Subsystem can accept requests either from the facade or client directly.
In any case, to the Subsystem, the Facade is yet another client, and it's
not a part of the Subsystem.
"""
def operation1(self) -> str:
return "Subsystem1: Ready!"
# ...
def operation_n(self) -> str:
return "Subsystem1: Go!"
class Subsystem2:
"""
Some facades can work with multiple subsystems at the same time.
"""
def operation1(self) -> str:
return "Subsystem2: Get ready!"
# ...
def operation_z(self) -> str:
return "Subsystem2: Fire!"
def client_code(facade: Facade) -> None:
"""
The client code works with complex subsystems through a simple interface
provided by the Facade. When a facade manages the lifecycle of the
subsystem, the client might not even know about the existence of the
subsystem. This approach lets you keep the complexity under control.
"""
print(facade.operation(), end="")
if __name__ == "__main__":
# The client code may have some of the subsystem's objects already created.
# In this case, it might be worthwhile to initialize the Facade with these
# objects instead of letting the Facade create new instances.
subsystem1 = Subsystem1()
subsystem2 = Subsystem2()
facade = Facade(subsystem1, subsystem2)
client_code(facade)
The façade interface implementation itself sits one abstracted level above the experiment configuration layer and can fully control apps. Additionally, UW methods within the façade such as set_config can be exposed for individual apps to implement on their own.
The façade 'experiment_manager.py' can be called to handle the interfacing to the config subsystems of each app. When a user provides input about which app and the experiment configuration for that app, the façade will translate the input information and call the appropriate configuration subsystem to set up the experiment as configured.
This will be added in a series of development stages:
- First, the base structure of the façade will call the configuration subsystem of the given App directly
- Next, the existing App configuration subsystem will be reduced in scope as UW Components are switched on to perform equivalent tasks. The tasks to be covered by this façade include:
- load config
- validate config
- create experiment directory
- create workflow manager files
- link fix files
- Over time, as new component drivers are added, they will be called upon to handle the validation of the user config that falls within their scope of a subcomponent.
Discussion and feedback pages for the wiki can be found here
[^1]: Shvets, O. Facade. refactoring.guru. Retrieved May 24, 2023, from https://refactoring.guru/design-patterns/facade