1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/thoughtworks-aggregate-persistence

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Преобразование объекта в модель предметной области

3.1 Запрос заказа

Приведённый ниже код используется для запроса заказа и возвращает Aggregate<Order>. После запроса к базе данных и создания Order-агрегата вызывается AggregateFactory.createAggregate для создания объекта Aggregate<T>. Внутри Aggregate<T>, он автоматически сохраняет снимок Order для последующего сравнения.

public Aggregate<Order> findById(String id) {
    OrderDO orderDO = orderMapper.selectByPrimaryKey(id);
    if (orderDO == null) {
        throw new EntityNotFoundException("Order(" + id + ") not found"););
    }

    Order order = orderDO.toOrder();
    order.setCustomer(customerRepository.findById(orderDO.getCustomerId()));
    order.setItems(getOrderItems(id));

    return AggregateFactory.createAggregate(order);
}

3.2 Сохранение нового заказа, изменение заказа

Интерфейс save используется для выполнения операций добавления, изменения и удаления строк заказа и заказа. Пример кода:

void save(Aggregate<Order> orderAggregate) {
    if (orderAggregate.isNew()) {
        //insert order
        Order order = orderAggregate.getRoot();
        orderMapper.insert(new OrderDO(order));
        //insert order items
        List<OrderItemDO> itemDOs = order.getItems().stream()
            .map(item -> new OrderItemDO(order.getId(), item))
            .collect(Collectors.toList());
        orderItemMapper.insertAll(itemDOs);
    } else if (orderAggregate.isChanged()) {
        //update order 
        updateAggregateRoot(orderAggregate);
        //delete the removed order items from DB
        removeOrderItems(orderAggregate);
        //update the changed order items
        updateOrderItems(orderAggregate);
        //insert the new order items into DB
        insertOrderItems(orderAggregate);
    }
}

В этом примере кода, когда orderAggregate.isNew() истинно, данные вставляются с помощью MyBatis Mapper. В противном случае, если агрегат был изменён, необходимо обновить данные.

Сначала обновляется корень агрегата. Объект домена (Order) сначала преобразуется в объект данных (OrderDO), затем DataObjectUtils сравнивает историческую версию OrderDO, чтобы получить значение Delta, и, наконец, обновляет базу данных с помощью метода обновления MyBatis select. Код выглядит следующим образом:

private void updateAggregateRoot(Aggregate<Order> orderAggregate) {
    //only update changed fields, avoid update all fields
    OrderDO newOrderDO = new OrderDO(orderAggregate.getRoot());
    Set<String> changedFields = DataObjectUtils.getChangedFields(orderAggregate.getRootSnapshot(), orderAggregate.getRoot());
    if (orderMapper.updateByPrimaryKeySelective(newOrderDO, changedFields) != 1) {
        throw new OptimisticLockException(String.format("Update order (%s) error, it's not found or changed by another user",
                orderAggregate.getRoot().getId()));
    }
}

Для добавления, удаления и изменения строк заказа используются методы Aggregate, а затем выполняются операции с базой данных. Пример кода выглядит следующим образом:

private void removeOrderItems(Aggregate<Order> orderAggregate) {
    Collection<OrderItem> removedEntities = orderAggregate.findRemovedEntities(Order::getItems, OrderItem::getId);
    removedEntities.stream().forEach((item) -> {
        if (orderItemMapper.deleteByPrimaryKey(item.getId()) != 1) {
            throw new OptimisticLockException(String.format("Delete order item (%d) error, it's not found", item.getId()));
        }
    });
}

private void updateOrderItems(Aggregate<Order> orderAggregate) {
    Collection<ChangedEntity<OrderItem>> entityPairs = orderAggregate.findChangedEntitiesWithOldValues(Order::getItems, OrderItem::getId);
    for (ChangedEntity<OrderItem> pair : entityPairs) {
        Set<String> changedFields = DataObjectUtils.getChangedFields(pair.getOldEntity(), pair.getNewEntity());
        OrderItemDO orderItemDO = new OrderItemDO(orderAggregate.getRoot().getId(), pair.getNewEntity());
        if (orderItemMapper.updateByPrimaryKeySelective(orderItemDO, changedFields) != 1) {
            throw new OptimisticLockException(String.format("Update order item (%d) error, it's not found", orderItemDO.getId()));
        }
    }
}

private void insertOrderItems(Aggregate<Order> orderAggregate) {
    Collection<OrderItem> newEntities = orderAggregate.findNewEntities(Order::getItems, (item) -> item.getId() == null);
    if (newEntities.size() > 0) {
        List<OrderItemDO> itemDOs = newEntities.stream().map(item -> new OrderItemDO(orderAggregate.getRoot().getId(), item)).collect(Collectors.toList());
        orderItemMapper.insertAll(itemDOs);
    }
}
``` Агрегатные методы `findXXXEntities`, предоставляемые `Aggregate<T>`, предназначены для работы с наборами сущностей, такими как строки деталей заказа. Например, в детали заказа может быть добавлен товар A, изменено количество товара B или удалён товар C. Методы `findXXXEntities` используются для обнаружения этих изменений.

Первый параметр  это функциональный интерфейс, который используется для получения набора сущностей и идентификации новых, изменённых или удалённых сущностей в этом наборе. Второй параметр также является функциональным интерфейсом, который получает значение первичного ключа сущности.

Следует отметить, что при изменении агрегата необходимо обновить номер версии корня агрегата независимо от того, изменился ли корень агрегата. Это гарантирует, что агрегат будет обновлён как единое целое и предотвратит возникновение несогласованности данных при параллельных изменениях.

### 3.3 Удаление заказа

При удалении заказа необходимо также удалить все строки деталей этого заказа.
```java
public void remove(Aggregate<Order> aggregate) {
    Order order = aggregate.getRoot();
    if (orderMapper.delete(new OrderDO(order)) != 1) {
        throw new OptimisticLockException(
            String.format("Delete order (%s) error, it's not found or changed by another user", order.getId())
        );
    }
    orderItemMapper.deleteByOrderId(order.getId());
}

Полный пример кода можно найти в проекте «Агрегатная персистентность» на GitHub (https://github.com/meixuesong/aggregate-persistence-sample). Этот проект демонстрирует, как использовать Mybatis для реализации агрегатной персистентности и сохранять только изменённые данные. Например, если в таблице есть 20 полей и изменяется только одно поле, то при использовании этого подхода будет изменено только одно поле в базе данных, а не все поля.

4. Заключение

В целом, этот проект предлагает облегчённое решение для агрегатной персистентности, которое помогает разработчикам создавать чистые доменные модели и хорошо поддерживает работу репозиториев. Используя моментальный снимок корня агрегата, Aggregate<T> может определить, какие изменения произошли в агрегате, после чего репозиторий использует оптимистичную блокировку на основе версии и функцию сравнения свойств DataObjectUtils для выборочного обновления базы данных.

5. Changelog

Версия 1.2: Изменён способ определения изменений. Ранее использовался метод сравнения значений полей, и если значение было равно null, изменение не определялось. В новой версии используется метод DataObjectUtils.getChangedFields для получения имён изменённых полей.

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Цель этого проекта — предоставить облегчённое решение для агрегирования и сохранения данных, которое поможет разработчикам проектировать модель предметной области, исходя из бизнес-потребностей, без необходимости задумываться о сохранении данных. При реализации персистентности репозитория не нужно учитывать бизнес-логику, достаточно сосредоточи... Развернуть Свернуть
Apache-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/thoughtworks-aggregate-persistence.git
git@api.gitlife.ru:oschina-mirror/thoughtworks-aggregate-persistence.git
oschina-mirror
thoughtworks-aggregate-persistence
thoughtworks-aggregate-persistence
master