Паттерн Repository представляет собой абстракцию слоя доступа к данным, позволяющую отделить логику приложения от деталей хранения и получения данных. В контексте Knockout.js это особенно важно, так как фреймворк ориентирован на построение реактивного пользовательского интерфейса, а Repository позволяет организовать чистую архитектуру между ViewModel и источниками данных (REST API, локальное хранилище, WebSocket и др.).
1. Абстрагирование источника данных Repository предоставляет методы для работы с объектами предметной области, не раскрывая, как именно данные извлекаются или сохраняются. Например, один и тот же интерфейс Repository может использовать локальный массив для тестов и HTTP-запросы к серверу в продакшене.
2. Универсальные методы CRUD Типичные методы Repository включают:
getAll() — получение всех сущностей;getById(id) — получение сущности по
идентификатору;create(entity) — добавление новой сущности;update(entity) — обновление существующей сущности;delete(id) — удаление сущности.Эти методы возвращают Promise, что позволяет работать с асинхронными источниками данных.
function UserRepository(apiClient) {
this.apiClient = apiClient;
this.getAll = function() {
return this.apiClient.get('/users')
.then(response => response.data);
};
this.getById = function(id) {
return this.apiClient.get(`/users/${id}`)
.then(response => response.data);
};
this.create = function(user) {
return this.apiClient.post('/users', user)
.then(response => response.data);
};
this.update = function(user) {
return this.apiClient.put(`/users/${user.id}`, user)
.then(response => response.data);
};
this.delete = function(id) {
return this.apiClient.delete(`/users/${id}`)
.then(response => response.data);
};
}
Ключевой момент: Repository не зависит от конкретного ViewModel. Он только предоставляет данные в структурированном виде.
ViewModel в Knockout.js работает с наблюдаемыми объектами
(ko.observable, ko.observableArray).
Repository обеспечивает данные, а ViewModel связывает их с
интерфейсом.
function UserViewModel(userRepository) {
var self = this;
self.users = ko.observableArray([]);
self.selectedUser = ko.observable(null);
self.loadUsers = function() {
userRepository.getAll()
.then(users => self.users(users));
};
self.addUser = function(user) {
userRepository.create(user)
.then(newUser => self.users.push(newUser));
};
self.updateUser = function(user) {
userRepository.update(user)
.then(updatedUser => {
var index = self.users().findIndex(u => u.id === updatedUser.id);
if (index > -1) self.users.splice(index, 1, updatedUser);
});
};
self.deleteUser = function(user) {
userRepository.delete(user.id)
.then(() => self.users.remove(user));
};
}
Особенности использования:
ko.observableArray автоматически обновляют
UI.Promise позволяет легко
интегрировать разные источники данных.Для повышения производительности можно реализовать кэширование данных внутри Repository. Например:
function CachedUserRepository(apiClient) {
var cache = [];
this.getAll = function() {
if (cache.length) return Promise.resolve(cache);
return apiClient.get('/users')
.then(response => {
cache = response.data;
return cache;
});
};
this.getById = function(id) {
var cachedUser = cache.find(u => u.id === id);
if (cachedUser) return Promise.resolve(cachedUser);
return apiClient.get(`/users/${id}`)
.then(response => response.data);
};
this.create = function(user) {
return apiClient.post('/users', user)
.then(response => {
cache.push(response.data);
return response.data;
});
};
this.update = function(user) {
return apiClient.put(`/users/${user.id}`, user)
.then(response => {
var index = cache.findIndex(u => u.id === user.id);
if (index > -1) cache.splice(index, 1, response.data);
return response.data;
});
};
this.delete = function(id) {
return apiClient.delete(`/users/${id}`)
.then(() => {
var index = cache.findIndex(u => u.id === id);
if (index > -1) cache.splice(index, 1);
});
};
}
Преимущества кэширования:
Knockout.js поддерживает реактивное обновление UI при изменении наблюдаемых значений. При интеграции с Repository важно учитывать:
Promise для асинхронных запросов.ko.observable или
ko.observableArray только после успешного получения
данных..catch() для предотвращения
некорректного состояния ViewModel.self.loadUsers = function() {
userRepository.getAll()
.then(users => self.users(users))
.catch(error => console.error("Ошибка загрузки пользователей:", error));
};
В крупных приложениях рекомендуется: