// предметная область
// собственно элементы нашей бизнес логики
// или формат данных нашего приложения
// именно в таком виде приложение видит данные
function Item(title) {
this.title = title;
}
Item.prototype = {
getPrice: function () {
return this.price;
},
setPrice: function (price) {
this.price = price;
},
setDiscount: function (discount) {
this.discount = discount;
},
getDiscount: function () {
return discount;
}
};
function OrderItem(item) {
this.item = item;
// запоминаем стоимость товара на момент покупки
// так как за время жизни заказа стоимость и скидка
// может несколько раз поменяться
this.price = item.getPrice() * item.getDiscount();
}
OrderItem.prototype = {
getPrice: function () {
return ;
},
getItem: function () {
return this.item;
}
}
function Order()
{
this.createdAt = new Date();
this.items = [];
}
Order.prototype = {
// вот тут я не уверен, следует ли создавать OrderItem внутри этого метода...
addItem: function (item) {
this.items.push(new OrderItem(item));
},
// согласно принципу "информационный эксперт" мы должны работать с данными
// там где они есть
// то есть создавать отдельный компонет для подсчета стоимости - ошибка
getPrice: function () {
return this.items.reduce(function (sum, orderItem) {
return sum + orderItem.getPrice();
}, 0);
}
}
// представление наших данных
// эти штуки знают о том как преобразовать данные из предметной области
// в требуемый формат.
// по хорошему у нас должны быть ItemView и OrderItemView но мне лень...
// так же этот класс должен содержать какие-то методы хелперы по преобразованию данных
// например цену оно должно брать именно из order item а не из item....
// но тут много вариантов и мне опять же лень... Все зависит от бизнес логики
// допустим у форм свои View
function OrderView(order, renderer) {
this.order = order;
// это какой-то сервис, который умеет рендрить шаблоны
// мы не знаем какие шаблоны и как оно работает
// мы только знаем что у него есть метод render
this.renderer = renderer;
}
OrderView.prototype = {
render: function () {
return this.renderer.render(this.order);
}
};
// сервисный слой
// хранилище наших заказов
// именно под этим кроется базы данных, апишки, кеши и прочее
// но суть именно такая, как тут. У нас есть некий объект-репозиторий
// который хранит внутри себя сущности
// снаружи систмы мы понятия не имеем что он сотворил с данными
// сериализовал, отправил на сервер, сохранил в локальной базе...
// нам нету дела. Зато если мы запросим у него объект
// он вернет нам его в том же состоянии, в котором мы его туда ложили
function OrderRepository(){
this.oriders = [];
}
OrderRepository.prototype = {
remove: function (order) {
// ...
},
find: function (id) {
// ...
},
save: function (order) {
// ...
}
}
// контроллер
// согласно все тем же принципам GRASP
// отвечает за обработку пользовательских сценариев
// вся работа с вводом/выводом пользовательских данных должна выполняться тут
// для удобства представим что у нас есть dependency injection
// который внедрит все зависимости по названию аргументов :)
function addOrderController(request, security, renderer, orderRepository, itemRepository) {
var user = security.getUser(),
item = itemRepository.find(requres.get('id'));
// обработкой и вывводом ошибок занимается фронт-контроллер.
// чем слоенее наш пирок, тем он гибче
if (!item) {
throw "Item not found";
}
var order = new Order();
order.addItem(item);
orderRepository.save(order);
// тут можно намного удобнее сделать, просто показал суть иерархии.
// Что это может быть не только шаблоны
return new Response((new OrderView(order, renderer)).render(), 200);
}