The Abstract Model App is a TypeScript-based application that provides a flexible and extensible framework for creating, retrieving, updating, and deleting model instances using different storage strategies. It demonstrates how to use abstract classes, generics, and TypeScript’s advanced type system to build a scalable and maintainable codebase.
The project includes:
- Abstract Models: A base class that defines common properties and methods for all models.
- Specific Model Implementations: Such as the User model, extending the abstract model.
- Model Registry: A system to manage model types and their options.
- Multiple Storage Strategies: Including In-Memory, Local Storage, and IndexedDB, which can be swapped seamlessly.
- Unified API: An App class providing high-level methods for interacting with models and storage.
- Separation of Concerns: Models, storage strategies, and application logic are decoupled for modularity.
- Type Safety: Extensive use of TypeScript’s generics and type inference ensures robust and reliable code.
- Flexibility: Swap storage strategies without changing core application logic.
- Extensibility: Easily add new models and storage strategies.
- Reusability: Common code is abstracted into base classes and interfaces.
- Unified CRUD Operations: Consistent methods for Create, Read, Update, and Delete across models.
- Customizable ID Generation: Utilizes the storage strategy’s ID generation or falls back to UUIDs.
Benefits of This Approach
- Modularity: Promotes cleaner code architecture by separating different concerns.
- Scalability: Simplifies adding new features or models without significant refactoring.
- Maintainability: Easier to manage and update individual components.
- Enhanced Developer Experience: TypeScript’s tooling provides IntelliSense and compile-time checks.
- Adaptability: Quickly switch storage mechanisms to suit different environments or requirements.
- app.ts: Main application logic to create, retrieve, update, and delete model instances.
- main.ts: Entry point of the application, demonstrating usage of the App class.
- models/: Contains abstract model definitions and specific model implementations.
- abstract.model.ts: Abstract model class with common functionality for all models.
- user.model.ts: User model extending the abstract model.
- model.register.ts: Registry of all model types and their respective options and collection paths.
- storage/: Storage strategy implementations.
- storage.strategy.ts: Interface for storage strategies.
- in-memory.strategy.ts: In-Memory storage strategy implementation.
- local-storage.strategy.ts: Local Storage strategy implementation.
- idb.strategy.ts: IndexedDB storage strategy implementation.
- storage.ts: Storage class handling the selection and usage of storage strategies.
- Node.js (version 14 or above)
- npm (version 6 or above)
- TypeScript (installed globally or as a dev dependency)
- Clone the repository:
git clone https://github.com/your-repository/abstract-model-app.git
- Navigate to the project directory:
cd abstract-model-app
- Install the dependencies:
npm install
- Storage Strategy: Adjust the storage strategy by modifying the setStrategy method in storage.ts or via the App class.
- Default is In-Memory (InMemoryStorageStrategy).
- ID Generation: The Storage class includes a generateId method that uses the storage strategy’s ID generation or defaults to UUIDs.
Include a task in your .vscode/tasks.json to build and run the project.
- Open the project in VSCode.
- Open the terminal in VSCode and run the following command to compile TypeScript files:
npx tsc
- Execute the compiled JavaScript file:
node dist/main.js
- Alternatively, use the VSCode task:
- Open the Command Palette (
Ctrl+Shift+P
). - Select
Tasks: Run Task
. - Choose
Build and Run
.
- Open the Command Palette (
- The application logs operations to the console.
- Run the application and check the console output to verify CRUD operations.
The application demonstrates creating, retrieving, updating, and deleting users using the App class. You can interact with the App class to perform these operations.
- Create the Model Class:
- Add a new file in src/models/ extending AbstractModel.
- Update the Model Registry:
- Modify model.register.ts to include the new model.
- Update Storage Mappings:
- Add a new entry to ModelKeyToCollectionKey.
- Create the Strategy Class:
- Add a new file in src/storage/ implementing StorageStrategy.
- Update the Storage Class:
- Import and incorporate the new strategy in storage.ts.
- Use setStrategy to switch to the new strategy.
import { App } from './app';
const app = new App();
async function run() {
// Create and save a user
const userJohn = await app.create('user', {
state: {
name: 'John Doe',
email: 'john.doe@example.com'
},
saveToStorage: true
});
console.log('User John created:', userJohn);
const userJane = await app.create('user', {
state: {
name: 'Jane Doe',
email: 'jane.doe@example.com'
},
saveToStorage: true
});
console.log('User Jane created:', userJane);
// Get all users
const users = await app.get('users');
console.log('All users:', users);
// Retrieve a user by ID
const retrievedUserJohn = await app.get('user', { id: userJohn.id });
console.log('User retrieved:', retrievedUserJohn);
// Update the user
if (retrievedUserJohn) {
retrievedUserJohn.name = 'John Doh';
retrievedUserJohn.email = 'john.doh@example.com';
await app.update(retrievedUserJohn);
console.log('User updated:', retrievedUserJohn);
}
// Retrieve the updated user
const updatedUserJohn = await app.get('user', { id: userJohn.id });
console.log('Updated user retrieved:', updatedUserJohn);
// Delete the user
await app.delete('user', userJohn.id);
console.log('User deleted');
// Try to retrieve the deleted user
const deletedUserJohn = await app.get('user', { id: userJohn.id });
console.log('Deleted user retrieved:', deletedUserJohn);
// Get all users after deletion
const updatedUsers = await app.get('users');
console.log('All users after deletion:', updatedUsers);
}
run();
Contributions are welcome! Please open issues or submit pull requests for improvements and bug fixes.
This project is licensed under the MIT License.