- Infrastructure.Interface (to share common definitions like command names, constants, etc.)
- Infrastructure.Layout (to encapsulate the screen real estate into different workspaces)
- Infrastructure.Library (a set of commonly used helper functions)
- Shell (the executable)
- Litware.HR (the module itself)
- A couple projects under "Original" folder. (These are mainly the data contracts for the online services)
The most interesting code is under of course Litware.HR. All main logic is contained in this assembly: this is where all views, presenters, core services and generally speaking all "business logic" is.
Being a CAB based solution, LitwareHR-SC consists of a single "CAB module" which is hosted by a Shell. A default shell is provided, but you can re-host the module anywhere if you provide the logical workspaces the module expects.
There are roughly 3 layers implemented in Litware.HR as illustrated in Figure 1.
- The "Service Agents" and "Proxies", whose main purpose is abstract communication with LitwareHR on-line services. Service Agents are like regular web proxies but extended to support offline behavior through artifacts supplied by SC-SF (e.g. queuing, dispatching, persistence, etc).
- The "Services" (as in "CAB Services") layer which is a collection of singleton components that provide core services for the app. There are two classes of "Services":
- Non-LitwareHR specific: like DB access, Managing connections, etc. (the orange ones in Figure 1)
- LitwareHR specific (blue boxes): Tenant Branding, Recruiting and Process. These are roughly the "Model" in "Model-View-Presenter"
- The "Views" layer, which implement the visuals of the app. Each view has a corresponding Presenter class that encapsulates the logic that drives that view and interacts with the "Model".
Figure 1: LitwareHR Smart Client Architecture
The app makes extensive use of CAB Events for notification of important changes such as: connectivity changes, branding and style changes (new logo), meta-data changes, etc. Most events are published by the Services and consumed by either other Services, Presenters and the Shell. This is the way the status bar in the Shell changes from "Online" to "Offline" for example.
Look for any [EventPublication] attribute in the code.
The best way of understanding how the app is initialized is by looking at the Run method in the ModuleController class of Litware.HR. This is the "main" function for a CAB module generated by the SC-SF.
Placing a few breakpoints and stepping over the code here will give you an idea of how things are wired together and what depends on what.
Almost all services are built with a similar pattern:
- Private members
- [InjectionConstructor] class constructor with [ServiceDependency] parameters
- A public API (events, methods, properties)
Figure 2: Tenant Branding Service code snippet
In this case, when added to the (root) WorkItem, the TenantBrandingService will be initialized with 3 dependent services: PresentationService, ConnectionMonitor and the AuthenticationService. It also publishes 2 events:
- Progress information while downloading the Tenant logo
- A new logo is available for display (it will of course be up to the subscribers of these event what to do with them)
The Recruiting Service is the main business logic service. You will notice it has LitwareHR semantics (e.g. Open Positions, Get Positions, etc) but above all this is where the switching logic is for retrieving data either from the on-line services or the local store depending on network availability. The strategy followed in the app is to always try to interact with the on-line services and fallback to the offline store if that fails. There's plenty of room for more advanced strategies like using the offline store as a cache if the requests are received within a short time window, etc.
The meta-model (entities & views definitions) are updated every time connectivity is a change in the connectivity status. This means that each time you disconnect & connect the data & views schemas are retrieved from LitwareHR on-line services and compared against the local ones. If there's any difference an update is done and the corresponding event is raised.
Branding (exemplified through the header logo) is updated "real-time". Under the hood, the branding service polls for changes in the configured style and if it detects changes against the current one, it will download the new logo. All this is done asynchronously so the UI remains responsive throughout all the process.
The next articles will drill-down on specifics aspects of the solution that are interesting.