-
Notifications
You must be signed in to change notification settings - Fork 1
Architectural Overview
The application architecture presents an integrated framework that seamlessly fuses the principles of Domain-Driven Design (DDD), Event Sourcing, and Command Query Responsibility Segregation (CQRS) with the robust capabilities of Redux. Utilizing PouchDB for state persistence, the architecture delivers an event-driven, state-cached, isomorphic application infrastructure. This framework is adept at handling both client-side and server-side functionalities and allows for real-time synchronization and/or delegation between various combinations of client and server instances.
-
Redux: Functions as the cornerstone for state management.
- Actions: Serve dual roles as commands from external systems and as internal domain events.
- Middleware: Manages event sourcing, state synchronization, and state snapshotting.
- Thunks: Action handlers that act as conduits, directing command actions towards application services and domain event actions towards domain services within the DDD layer.
- Listeners: Function as sagas within the DDD layer.
- EntityAdapters: Handle all state change domain events within the DDD layer.
- Selectors: Function as query interfaces and play a crucial role in domain state initialization.
-
Domain-Driven Design (DDD): Serves the business layer.
- Entities and Aggregate Roots: Are instantiated and overseen by a Factory layer, which translates state data into domain-centric entities and emits change state domain events.
- Domain Services: Envelop complex business logic and are activated by Thunks or Listeners and emits non change state domain events.
- Application Services: Administer application-level transaction logic and are orchestrated by either Thunks or Listeners and emit non change state domain events.
- Domain Events: Are significant business milestones captured through actions, capable of initiating a change of state, other domain services and sagas.
- Repository: Realized through Redux's Entity Adapter, it facilitates the selectors and handling data update domain events.
- Sagas: Implemented as Listeners, these model intricate, long-term business processes.
-
CQRS: Forms the user interface layer.
- Commands: Are actions initiated via the UI to trigger system state alterations through internal DDD business logic.
- Queries: Implemented through Selectors, they furnish precise, real-time system state information to the UI.
-
PouchDB: Serves as the persistence mechanism.
- State Restoration: Enables the recovery of the initial entity state at application launch.
- State Snapshot: Enables the recovery of the entire store state from a specific point.
- Event Sourcing: Records and replays pivotal state transitions.
- Store Synchronization: Permits a plurality of stores to maintain congruent local states.
- Backup & Redundancy: Permits replication of data which can serve as a live backup and facilitate redundancy in case of failures.
- Initialization: At application start-up or upon server request, the state is either initialized or retrieved from PouchDB entity cache directly, or through a snapshot and event replay, rendering the system both resilient and rapid to deploy.
- User Interaction: Users engage with the system, prompting the dispatch of Redux command actions.
- Command Handling: Thunks serve as the command handlers, invoking suitable DDD application services.
- Domain Logic Execution: Application services coordinate entities and domain services to execute business logic, possibly emitting Domain Events.
- Event Handling: Thunks, Listeners and the Entity Adapter function as event handlers; Thunks activate domain services, Listeners operate as sagas whereas the Entity Adapter changes the state in the store.
- State Updates: Alterations to state are channeled via domain event actions through the Redux Entity Adapter, which consequently updates the corresponding PouchDB databases for durable storage.
- Event Sourcing: The architecture preserves a chronological log of key state transition actions for auditing or state reconstitution purposes.
- State Snapshotting: The architecture takes a snapshot of the entire state at specifically desired point in time, allowing for quicker restoration from event replay starting from that specifically captured point in time.
- Querying: Business-related queries are executed through Selectors, serving effectively as read-models.
- Server-Client Symmetry: The isomorphic nature of the architecture facilitates fluid transitions between client-side and server-side executions.
- REST Integration: The architecture can optionally present itself as a RESTful service, enhancing its versatility in mixed-technology landscapes.
- Real-time Synchronization: Designed for real-time state synchronization, the architecture supports concurrent state across multiple instances, irrespective of whether they are client or server-based.
- Server-Client-Proxy Complementarity: Allows instances of itself to serve as Clients, Servers and Proxies that integrate into a network that has redundancy and work delegation across all its nodes.
The presentation architecture offers a seamless framework that is agnostic to its deployment environment—be it a web client in the form of a Progressive Web Application (PWA), Single Page Application (SPA), or Multi-Page Application (MPA), an API, or a Proxy server. Leveraging the sophisticated capabilities provided by React, this architecture capitalizes on advanced data APIs offered by React Router. These APIs facilitate communication with the application layer independently while transmitting the information to the presentation components or to the underlying server using FetchAPI requests and responses. For server-side scenarios, facilitated by Express, the architecture first transforms the request into a FetchAPI request and employs React Router's static handlers, invoking the router's loader or action in a manner similar to client-side behavior. This provides a response along with a render context; for JSON responses, the response is directly forwarded to the underlying server and processed for sending an express response to the client, and for server-side rendering, the render context is used to generate markup, which is then sent to the client. On both the client and server sides, the data responded with is made available as a JSON object through a hook, thanks to the context object unpacking the response automatically on the server, and the React Router runtime doing so on the client. Consequently, the architecture supports isomorphic application infrastructure, adeptly managing both client-side and server-side interfaces and enabling seamless delegation between various client and server instances.
-
React Router (>=6.4): Functions as the cornerstone for presentation state management.
- Application UI: Dispatches command actions to change application state and uses selectors to query application state.
- Translation: Transforms data returned from selectors to service the requested presentation (HTTP, HTML, JSONAPI, etc...).
-
React (>=18.2): Encompasses the visual presentation logic using HTML, JSX and and CSS.
- Presentation UI: Obtains presentation information from the router and uses it to construct screens for interaction
- User Interaction: Interprets user onscreen actions and submits the information to the router for processing.
-
Express: Provides API endpoints and client functionality on the server through Server Side Rendering.
- Initialization: At application start-up or upon server request, the state is either initialized or hydrated, rendering the system both resilient and rapid to render.
- User Interaction: Users engage with the rendered interface screen, prompting the invocation of actions.
- Querying: Business-related queries are executed through Selectors, invoked and responded with by loaders.
- Client Reactivity: On the client the screen reacts to the new state while on the server a new screen is rendered and sent as a response.
- Server-Client Symmetry: The isomorphic nature of the architecture facilitates fluid transitions between client-side and server-side rendering.
- REST Integration: The architecture can optionally present itself as a RESTful service, enhancing its versatility in mixed-technology landscapes.
- Server-Client-Proxy Complementarity: Allows instances of itself to serve as Clients, Servers and Proxies that integrate into a network that has redundancy and work delegation across all its nodes.
-
Platform Agnosticism: The application can be deployed
- as a Progressive Web Application client on a device
- as an Single Page Application on modern browsers with JavaScript enabled
- as Multi Page Application on modern browsers with JavaScript disabled
- as REST API servers that service PWA and SPA clients
- as REST API Proxy servers that delegate work to another REST API instance.
Introduction
- Electron
- Capacitor
-
Platform Agnosticism: The application can be deployed
- as a Native Desktop client on Windows, MacOS and Linux.
- as a Native Mobile client on Android and iOS.
The persistence architecture is designed to utilize the robust capabilities of PouchDB and CouchDB in a complementary fashion. This integration allows for a broad range of persistence configurations, from standalone clients running on native platforms with self-contained data storage, to distributed systems that utilize CouchDB's multi-node, real-time data synchronization across the web. The architecture supports various configurations, whether they are fully isolated, fully distributed, or any hybrid of the two. By leveraging PouchDB's local storage, offline capabilities, replication and synchronization with CouchDB's distributed database features, the persistence layer provides a scalable, reliable, and adaptable data storage and synchronization between any association of client and server instances.
-
CouchDB: Functions as the backend database instance container.
- Sync: Synchronization with a CouchDB instance on a server allows the same data to exist in parallel as multiple instances.
- Distributed: Distribution across many servers allows the same database to exist in parallel as multiple instances.
- PouchDB
- Sync: PouchDB can synchronize with any other PouchDB or CouchDB instance it has access to which means the same client application data can exist in both server and client contexts allowing the client to be active in those contexts at the same time.