Event-Driven Architecture with Mediator Topology for managing Chat Bookmarks on marketplace.
- Hexagonal Architecture - Core domain logic isolated from infrastructure
- Event-Driven Architecture (EDA) with Mediator Topology
- Singleton Pattern - Application and Service Locator
- Repository Pattern - Data access abstraction
- Strategy Pattern - Switchable storage backends (REST API / LocalStorage)
- Gateway Pattern - External API communication
- Anti-Corruption Layer (ACL) - Two-Way Mapping for data translation
- Actor Model - State management with message passing
- Service Locator - Dependency resolution
src/
├── core/
│ ├── singleton/
│ │ └── Application.ts # Main application singleton
│ └── locator/
│ └── ServiceLocator.ts # Service locator pattern
├── domain/
│ └── entities.ts # Domain entities and interfaces
├── infrastructure/
│ ├── gateway/
│ │ └── RestApiGateway.ts # REST API gateway implementation
│ ├── strategy/
│ │ ├── LocalStorageStrategy.ts
│ │ └── RestApiStrategy.ts
│ ├── acl/
│ │ └── AntiCorruptionLayer.ts # ACL with two-way mapping
│ └── mapper/
│ └── BookmarkMapper.ts # Entity-DTO mapper
├── application/
│ └── services/
│ └── BookmarkRepositoryImpl.ts # Repository implementation
└── ui/
├── mediator/
│ └── EventMediator.ts # Event mediator (singleton)
├── channels/
│ └── EventChannel.ts # Event channel for routing
├── processors/
│ └── EventProcessors.ts # Event processors
├── actors/
│ └── Actors.ts # Actor model implementation
└── components/
└── UIAdapter.ts # Framework-agnostic UI adapter
interface ChatBookmark {
id?: string;
title: string;
description: string;
isActual: boolean;
addTime: Date;
authorUuid: string;
}- CRUD Operations: Create, Read, Update, Delete bookmarks
- Optimistic Updates: UI updates immediately, syncs with backend asynchronously
- SessionStorage: UI state persisted in browser SessionStorage
- Strategy Switching: Toggle between REST API and LocalStorage at runtime
- Framework Agnostic: Works with React, jQuery, Svelte, or vanilla JS
import { uiAdapter } from './src/index.js';
// Initialize with LocalStorage (default)
await uiAdapter.initialize(false);
// Or initialize with REST API
await uiAdapter.initialize(true);const unsubscribe = uiAdapter.subscribe((state) => {
console.log('Bookmarks:', state.bookmarks);
console.log('Loading:', state.isLoading);
console.log('Error:', state.error);
});
// Later, unsubscribe when needed
unsubscribe();// Create
await uiAdapter.createBookmark({
title: 'New Bookmark',
description: 'Description here',
isActual: true,
addTime: new Date(),
authorUuid: 'user-123',
});
// Update
await uiAdapter.updateBookmark('bookmark-id', {
title: 'Updated Title',
isActual: false,
});
// Delete
await uiAdapter.deleteBookmark('bookmark-id');// Switch to REST API
await uiAdapter.switchStorageStrategy(true);
// Switch back to LocalStorage
await uiAdapter.switchStorageStrategy(false);import { useEffect, useState } from 'react';
import { uiAdapter, type UIState } from './src/index.js';
function BookmarkApp() {
const [state, setState] = useState<UIState | null>(null);
useEffect(() => {
uiAdapter.initialize(false).then(() => {
const unsubscribe = uiAdapter.subscribe(setState);
return unsubscribe;
});
}, []);
if (!state) return <div>Loading...</div>;
return (
<div>
<button onClick={() => uiAdapter.switchStorageStrategy(true)}>
Use REST API
</button>
<button onClick={() => uiAdapter.switchStorageStrategy(false)}>
Use LocalStorage
</button>
{state.bookmarks.map(bookmark => (
<div key={bookmark.id}>
<h3>{bookmark.title}</h3>
<p>{bookmark.description}</p>
</div>
))}
</div>
);
}import { uiAdapter } from './src/index.js';
$(async () => {
await uiAdapter.initialize(false);
uiAdapter.subscribe((state) => {
$('#bookmarks-list').empty();
state.bookmarks.forEach(bookmark => {
$('#bookmarks-list').append(`
<div class="bookmark">
<h3>${bookmark.title}</h3>
<p>${bookmark.description}</p>
</div>
`);
});
});
$('#add-bookmark-btn').click(async () => {
await uiAdapter.createBookmark({
title: $('#title').val(),
description: $('#description').val(),
isActual: true,
addTime: new Date(),
authorUuid: 'current-user',
});
});
});<script lang="ts">
import { onMount } from 'svelte';
import { uiAdapter, type UIState } from './src/index.js';
let state: UIState | null = null;
onMount(async () => {
await uiAdapter.initialize(false);
uiAdapter.subscribe(s => state = s);
});
async function createBookmark() {
await uiAdapter.createBookmark({
title: 'New',
description: 'Desc',
isActual: true,
addTime: new Date(),
authorUuid: 'user-1',
});
}
</script>
{#if state}
<button on:click={createBookmark}>Add Bookmark</button>
{#each state.bookmarks as bookmark}
<div>{bookmark.title}</div>
{/each}
{/if}- User Action → UI Adapter publishes event
- Event Mediator routes event to appropriate processor
- Event Processor handles business logic via Repository
- Repository uses current Strategy (REST API or LocalStorage)
- Strategy communicates via Gateway or directly stores
- ACL translates between external and internal formats
- Result Event published back through Mediator
- Actors update state optimistically
- UI re-renders based on state changes
npm install
npm run buildMIT