UOW изменение количественного свойства
От: VAD_OS  
Дата: 31.05.17 14:26
Оценка:
Подскажите как лучше всего используя unit of work отслеживать изменение количественного свойства(например количество товара).
К примеру есть доменная сущность

public class Product
{
public string Number{get;set;}
public string Name{get;set;}
.....
.....
public int Quantity{get;set;} //количество товара
}

Как исключить потерянное обновление на уровне бизнес транзакции?
То есть 2 пользователя одновременно решили купить товар,
оба получили значение Quantity = 3,
после этого сначала один поменял значение (3 — 1) = 2,
затем другой так же (3 — 1) = 2
получается что у нас сохранилось 2 вместо 1

Какие есть варианты решения данной задачи,
например добавить свойство NewQuantity,
чтобы UoW знал что сначала надо получить дельту нового
и старого значения и сохранял уже это изменение например с помощью транзакции БД?


Буду рад любым примерам и ссылкам, заранее спасибо.
Re: UOW изменение количественного свойства
От: vsb Казахстан  
Дата: 31.05.17 15:23
Оценка:
Вариант 1. Использовать транзакции. Например встроенные в СУБД.
При этом тот, кто обновил свойство первым, его обновит, а второму покажет ошибку, мол вы отредактировали неактуальные данные, можно ему предложить, например, отредактировать ещё раз уже актуальные данные. Предполагается, что транзакций открывается при показе формы и коммитится/откатывается при закрытии формы. В принципе так сейчас не делают.

Вариант 2. Оптимистичные блокировки. Примерно то же самое, только руками. При открытии формы запоминается некая ревизия записи, при сохранении сравнивается с последней и сохраняется только если совпадает.

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

Вариант 3. Поток событий. Каждая единица работы это отдельное событие. В твоём случае не "Изменить 3 на 2", а "Уменьшить количество на единицу". Соответственно разницы нет, в каком порядке эти события пришли, результат будет одинаковый.
Re: UOW изменение количественного свойства
От: IT Россия linq2db.com
Дата: 31.05.17 15:40
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Подскажите как лучше всего используя unit of work отслеживать изменение количественного свойства(например количество товара).


Очевидно, что для одновременного изменения объекта из двух мест, необходимо использовать блокировку изменяемого ресурса на время изменения и проверки последствий такого изменения, если требуется. БД это делает автоматически — простые блокировки во время записи с проверкой констрейнов, либо транзакции, изоляции и прочие data integrity.

Если делать такие изменения на уровне приложения, например, веб-приложения, то необходимо реализовывать блокировки/проверки ручками. Если изменения производятся в двух разных процессах одной и той же машины, то можно для этого задействовать именованные мьютексы. Если на разных компьютерах, то нужно изобретать велосипед с использованием чего-нибудь общедоступного, файлы или та же БД.

В любом случае UoW во всех этих сценариях абсолютно мимо кассы, т.к. никакими средствами блокировки не обладает.
Если нам не помогут, то мы тоже никого не пощадим.
Re: UOW изменение количественного свойства
От: VAD_OS  
Дата: 02.06.17 08:31
Оценка:
Уточню вопрос, как это сделать средствами БД я понимаю. Здесь скорее вопрос по архитектуре, допустим мы еще не знаем,
что у нас будет использоваться база данных, хотелось бы увидеть пример кода (допустим для интернет магазина),
который при осуществлении покупки уменьшает количество товара, именно в стиле DDD.
Если unit of work здесь не подходит для отслеживания изменений, то возможно есть другой способ.
Подчеркну что для меня важно понять, как это можно сделать используя именно подход DDD.
Так же важно понять как это будет выглядить в коде, идея того что можно отслеживать не само значения, а на сколько оно изменилось понятна и так, интересно как это реализовать?
Отредактировано 04.06.2017 18:33 VAD_OS . Предыдущая версия . Еще …
Отредактировано 03.06.2017 10:43 VAD_OS . Предыдущая версия .
Отредактировано 02.06.2017 9:44 VAD_OS . Предыдущая версия .
Re[2]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 05.06.17 22:30
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Уточню вопрос, как это сделать средствами БД я понимаю. Здесь скорее вопрос по архитектуре, допустим мы еще не знаем,

VAD>что у нас будет использоваться база данных, хотелось бы увидеть пример кода (допустим для интернет магазина),
VAD>который при осуществлении покупки уменьшает количество товара, именно в стиле DDD.
Как уже отметили выше, надо либо привязывать бизнес транзакции к механизмам БД(транзакции, оптимистичная блокировка), что как правило, реализуется конфигурированием ORM-а, либо же городить свой велосипед. Я бы велосипедостроением в этом случае заниматься не рекомендовал бы, потому как тут грабли аккуратно разложены в огромном количестве и в процессе велосипедостроения, неизбежно придется по ним пройтись. Это долго, дорого, больно.
Re[3]: UOW изменение количественного свойства
От: VAD_OS  
Дата: 06.06.17 14:09
Оценка:
Здравствуйте, itslave, Вы писали:

I>Здравствуйте, VAD_OS, Вы писали:


VAD>>Уточню вопрос, как это сделать средствами БД я понимаю. Здесь скорее вопрос по архитектуре, допустим мы еще не знаем,

VAD>>что у нас будет использоваться база данных, хотелось бы увидеть пример кода (допустим для интернет магазина),
VAD>>который при осуществлении покупки уменьшает количество товара, именно в стиле DDD.
I>Как уже отметили выше, надо либо привязывать бизнес транзакции к механизмам БД(транзакции, оптимистичная блокировка), что как правило, реализуется конфигурированием ORM-а, либо же городить свой велосипед. Я бы велосипедостроением в этом случае заниматься не рекомендовал бы, потому как тут грабли аккуратно разложены в огромном количестве и в процессе велосипедостроения, неизбежно придется по ним пройтись. Это долго, дорого, больно.

Если нетрудно можно пример кода? Что бы понимать, как это будет выглядеть.
Re: UOW изменение количественного свойства
От: Sinix  
Дата: 06.06.17 14:37
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Подскажите как лучше всего используя unit of work отслеживать изменение количественного свойства(например количество товара).

VAD>К примеру есть доменная сущность

Вариант 1: сделать quantity вычисляемым

Вариант 2: завести бизнес-операцию "резерв товара", выполняемую как предусловие при процессинге заказа. В пару — периодический сброс "подвисших" товаров.
Скажем, запускать раз в 10 минут и отменять операции оформления заказа, которые висят дольше 15 минут.

Вариант 3: декремент через optimistic change tracking с обработкой неудачных попыток обновления.

Вариант 4: поднять проблему на уровень описания бизнес-процессов, формализовать решение, описать в ТЗ, сделать в коде
Re[4]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 07.06.17 07:27
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Если нетрудно можно пример кода? Что бы понимать, как это будет выглядеть.


ну к примеру:

class MyUnitOfWork
{
    private readonly _tr;
    public MyUnitOfWork(IDbConnection c)
    {
        _tr = c.CreateTransaction();
        _tr.BeginTransaction();
    }

    public void SaveChanges()
    {
        _c.Commit();
    }
    public void Dispose()
    {
        if (!_c.Commited)
            _c.Rollback();
    }
}
Re[2]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 07.06.17 07:29
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Вариант 3: декремент через optimistic change tracking с обработкой неудачных попыток обновления.



Тут у чувака непонимание как обрабатывать конкурентное изменение данных, ты слишком глубоко копаешь
ИМХА только третий вариант поможет, остальные только усугубят
Re[2]: UOW изменение количественного свойства
От: VAD_OS  
Дата: 07.06.17 11:35
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, VAD_OS, Вы писали:


VAD>>Подскажите как лучше всего используя unit of work отслеживать изменение количественного свойства(например количество товара).

VAD>>К примеру есть доменная сущность

S>Вариант 1: сделать quantity вычисляемым


S>Вариант 2: завести бизнес-операцию "резерв товара", выполняемую как предусловие при процессинге заказа. В пару — периодический сброс "подвисших" товаров.

S>Скажем, запускать раз в 10 минут и отменять операции оформления заказа, которые висят дольше 15 минут.

S>Вариант 3: декремент через optimistic change tracking с обработкой неудачных попыток обновления.


S>Вариант 4: поднять проблему на уровень описания бизнес-процессов, формализовать решение, описать в ТЗ, сделать в коде


1. Можно пример?
2. Я как раз и спрашиваю как такую операцию можно красиво реализовать))
3. Насколько я понимаю это встроенные средства БД, а если у нас её еще нет и мы решили реализовать прототип на фейках, а потом для каждого заказчика будем прикручивать свою базу, а кто-то может вообще захочет в файле хранить (опять же если я все правильно понял это не про DDD, но примеры интересны и для БД главное что бы реализации абстракций можно было переопределить например под хранение в файле или еще где-нибудь)
Re[5]: UOW изменение количественного свойства
От: VAD_OS  
Дата: 07.06.17 11:42
Оценка:
Здравствуйте, itslave, Вы писали:

I>Здравствуйте, VAD_OS, Вы писали:


VAD>>Если нетрудно можно пример кода? Что бы понимать, как это будет выглядеть.


I>ну к примеру:


I>
I>class MyUnitOfWork
I>{
I>    private readonly _tr;
I>    public MyUnitOfWork(IDbConnection c)
I>    {
I>        _tr = c.CreateTransaction();
I>        _tr.BeginTransaction();
I>    }

I>    public void SaveChanges()
I>    {
I>        _c.Commit();
I>    }
I>    public void Dispose()
I>    {
I>        if (!_c.Commited)
I>            _c.Rollback();
I>    }
I>}
I>


А как будет выглядеть доменная логика и как будет выглядеть отслеживание изменения свойства?
Re[6]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 07.06.17 13:17
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>А как будет выглядеть доменная логика и как будет выглядеть отслеживание изменения свойства?


Омг.
1. в к-р uow надо будет прокинуть https://msdn.microsoft.com/en-us/library/system.data.isolationlevel(v=vs.110).aspx с которым будет стартовать странзакция
2.
try
{
   using (var uow = new MyUnitOfWork(connection, IsolationLevel.Snapshot/* или любой другой который требуется логикой*/))
   {
       ....
       uow.SaveChanges(); 
   }
}
catch(Exception)
{
   ShowMessage("oops, someone have updated concurrently. Refresh the page to see actual information.")
}
Отредактировано 07.06.2017 19:53 itslave . Предыдущая версия . Еще …
Отредактировано 07.06.2017 13:21 itslave . Предыдущая версия .
Отредактировано 07.06.2017 13:17 itslave . Предыдущая версия .
Re[3]: UOW изменение количественного свойства
От: VAD_OS  
Дата: 08.06.17 14:53
Оценка:
Здравствуйте, itslave, Вы писали:

I>Здравствуйте, Sinix, Вы писали:


S>>Вариант 3: декремент через optimistic change tracking с обработкой неудачных попыток обновления.



I>Тут у чувака непонимание как обрабатывать конкурентное изменение данных, ты слишком глубоко копаешь

I>ИМХА только третий вариант поможет, остальные только усугубят

У меня не понятки как это сделать красиво с точки зрения архитектуры.

метод типа такого я могу написать

public void ChangeQuantity(int delta)
{
using(TransactionScope scope = new TransactionScope())
{
try{
//получаем текущее значение
// отнимаем дельту
// сохраняем то что получилось
scope.Complete()
}
catch(Exception){
scope.RollBack();
}
}
}

Мне нужен пример слабосвязанного кода, приведу пример.

например метод бизнес логики:

public class OrderProcessor: IOrderProcessor
{

......

public void ProcessOrder(int count)
{
//получаем продукт
....
product.NewQuantity = product.Quantity — count;
productRepository.Update(product);
}
}

public class Product{
.....
public int Quantity{get;set;}
public int NewQuantity{get;set;}
.......
}


//логика БД

DefaultProductREpository:IRepository<Product>
{
.....
public void Update(Product prod){
//открываем транзакцию
......
//получаем строку из таблицы, где хранится информация о количестве
var storageInfo = dbEntities.ProdStorageInfo.First(..... некое условие);
storInfo.Quantity = storInfo.Quantity — (product.Quantity — product.NewQuantity)

//работаем с другими данными для продукта которые хранятся в других таблицах

//сохраняем изменения закрываем транзакцию
....
}
....
}



Но такое решение наверное не очень красивое хотя бы потому,
что появляется дополнительное свойство у доменного объекта Product.
Re[4]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 08.06.17 16:18
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Мне нужен пример слабосвязанного кода, приведу пример.

VAD>Но такое решение наверное не очень красивое хотя бы потому,
VAD>что появляется дополнительное свойство у доменного объекта Product.
Это не слабосвязный код, и красивым его назвать нельзя хотя бы потому что он банально не решает поставленную задачу.

Я бы усиленно порекомендовал бы почитать по ключевым словам "оптимистическая блокировка", "транзакция", "пессимистическая блокировка". А после прояснения теорри, обратился бы в хелп любимого ORM-а и понял бы как упомянутые концепции реализованы в нем. Затем берешь код из примера, копипастишь, апдейтишь, возможно врапаешь красивыми интерфейсами(хотя в твоем случае — луччи не надо) и вуаля.
Re[5]: UOW изменение количественного свойства
От: VAD_OS  
Дата: 08.06.17 18:47
Оценка: :)
Здравствуйте, itslave, Вы писали:

I>Это не слабосвязный код, и красивым его назвать нельзя хотя бы потому что он банально не решает поставленную задачу


Я не утверждал что это красивый код, я наоборот пытался сказать что мне оно не нравится,
есть подозрение что ты сам не можешь предложить красивое решение потому что я просил не идеи как это в теории написать, а примеры кода
и привел сам пример того варианта, который мне не нравится что бы увидеть код от гуру DDD, если ты не такой подожди пока кто-нибудь даст дельный ответ заодно узнаешь как надо писать код
Re[6]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 08.06.17 19:47
Оценка:
Здравствуйте, VAD_OS, Вы писали:


VAD>есть подозрение что ты сам не можешь предложить красивое решение потому что я просил не идеи как это в теории написать, а примеры кода

теор основ ты не сумел нагуглить, но ожидаешь внеземного идеального кода. Ну ок. Блажен кто верует.
Re: UOW изменение количественного свойства
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.17 06:13
Оценка: 5 (2)
Здравствуйте, VAD_OS, Вы писали:

VAD>Буду рад любым примерам и ссылкам, заранее спасибо.

В DDD обычно доменные объекты сами "стучат" в транзакцию о том, что у них произошло изменение.
То есть код свойства Quantity устроен как-то так:
public int Quantity {
{
  get { return _quantity; }
  set {
    if (value != _quantity)
    {
      _transactionScope.TrackChange(this, "Quantity", _quantity, value);
      _quantity = value;
    }
  }
}


А дальше разбирается сам UnitOfWork.
Возможные варианты:
1. Optimistic locking. (самый популярный вариант). Трекер помнит состояние объекта на момент начала транзакции, и помечает все объекты как модифицированные (в примере используется только параметр this). При коммите он проверяет, что все изменённые объекты — такие же, как и в начале транзакции. Иначе вылетает "conflict" и транзакция откатывается.
2. Коммутативные операторы. Трекер записывает события-дельты, и при коммите выплёвывает их прямо в виде update products where id=@id set quantity = quantity + @delta. На практике не используется, т.к. работает только для типов с фиксированной запятой, и не позволяет отслеживать ограничения. Грубо говоря, если я хочу гарантировать, что Quantity >= 0, то в такой модели два конкурирующих потока запросто уменьшат количество с 5 до 1, и в результате в базу уедет (-3).
3. Пессимистичная блокировка. Перед каждой модификацией нам надо сделать select for update. На практике не используется по причине высокой вероятности deadlock, потому что порядок обхода объектов в lazy load гарантировать невозможно, и низкого быстродействия — ведь каждая модификация будет roundtrip к базе. И, опять же, нет никаких гарантий, что между первым select и нашей модификацией не вклинился кто-то ещё.

В общем, единственным мало-мальски жизнеспособным вариантом в DDD является оптимистическая блокировка. И то, она работает только в том случае, если принимать специальные меры при дизайне.
Там фишка в том, что с т.з. математики при оптимистической блокировке надо отслеживать прочитанные значения, а на практике это невозможно, и отслеживаются статусы только изменяемых объектов. В сценариях Quantity += Delta это работает потому, что читается и пишется один и тот же объект.
В сценариях типа OrderAmount = sum(OrderLine.Amount) это работает потому, что в объектах OrderLine при изменении их Amount принудительно пересчитывается OrderAmount. Если про это забыть, то между чтением OrderLine и записью OrderAmount запросто может вклиниться OrderLine update, и сумма ордера перестанет совпадать с суммой деталей.
В общем случае можно нарваться на полные неожиданности, когда рассчитанный агрегат не соответствует исходным данным ни на какой момент существования системы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: UOW изменение количественного свойства
От: Stalker. Австралия  
Дата: 01.07.17 01:09
Оценка:
Здравствуйте, VAD_OS, Вы писали:

VAD>Подскажите как лучше всего используя unit of work отслеживать изменение количественного свойства(например количество товара).

VAD>К примеру есть доменная сущность

странно, что за целые месяц нормально решения так никто и не выложил

1) Как уже сказали несколько раз (но почему-то без деталей реализации) можно применить оптимистическую блокировку. У SQL Server в таблицу добавляется поле rowversion, которое автоматически меняется при каждом апдейте. При чтении записи это поле сохраняется в модели, потом при сохранении делается что-то типа

update Product set bla-bla-bla where ProductID = @Id and RowVersion = @version

Если данная операция затронула 0 строк — поднимается сообщение об ошибке и юзеру обновляется экран с новой версией записи и предлагается провести операцию опять если товар еще остался.

2) Но в данном случае все куда проще т.к. наверняка бизнес-правило здесь не запрещать менять записи если кто-то уже что-то купил, а не дать пользователю увести количество товаров в ниже нуля. Это элементарно делается установкой констрейнта на поле таблицы Quantity >= 0. Теперь каждая транзакция просто производит апдейт записи на уменьшение товара на указанное количество и та, которая попытается увести количество в минус — получит исключение, которое и поднимается юзеру с откатом транзакции.

update Product set Quantity = Quantity — NumberOfProductsToBuy where ProductID = @Id

3) Такие задачи не решаются чистым DDD т.к. это конкурентные проблемы, DDD занимается совершенно другими вещами, а указанная проблема решается исключительно методами сервера базы данных наподобие указанных выше т.к. это его иепархия
Re[2]: UOW изменение количественного свойства
От: Vladek Россия Github
Дата: 03.07.17 17:30
Оценка: 2 (1)
Здравствуйте, VAD_OS, Вы писали:

VAD>Уточню вопрос, как это сделать средствами БД я понимаю. Здесь скорее вопрос по архитектуре, допустим мы еще не знаем,

VAD>что у нас будет использоваться база данных, хотелось бы увидеть пример кода (допустим для интернет магазина),
VAD>который при осуществлении покупки уменьшает количество товара, именно в стиле DDD.
VAD>Если unit of work здесь не подходит для отслеживания изменений, то возможно есть другой способ.
VAD>Подчеркну что для меня важно понять, как это можно сделать используя именно подход DDD.
VAD>Так же важно понять как это будет выглядить в коде, идея того что можно отслеживать не само значения, а на сколько оно изменилось понятна и так, интересно как это реализовать?

Тема достаточно хорошо освещена и гуглится по словам eventual consistency. Решается она по-разному, залезать в кишки серверов бд и мьютексов ОС не обязательно, это действительно вопрос грамотной архитектуры.

Вот один способ решения проблемы на примере банка: http://blog.sapiensworks.com/post/2016/07/23/DDD-Eventual-Consistency

С двумя покупателями проблемы нет, оба успешно купят товар, а запись в бд не их проблема, проблема с третьим и четвёртым покупателями. Один из них купит то, чего уже нет. Это нормальная ситуация в магазине. Тут надо просто выяснить у бизнеса, что делать с покупателем — увеличить срок доставки, вернуть деньги, что-то ещё. И запрограммировать именно то, чего ждёт бизнес, ублажение сервера бд всегда можно отложить на потом.
Re[3]: UOW изменение количественного свойства
От: itslave СССР  
Дата: 04.07.17 09:07
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Тема достаточно хорошо освещена и гуглится по словам eventual consistency. Решается она по-разному, залезать в кишки серверов бд и мьютексов ОС не обязательно, это действительно вопрос грамотной архитектуры.

Eventual consistency — это вообще о другом. Это когда у тебя распределенная система и обновления до разных узлов этой системы доходят с запозданием. Гуглить по фразам "CAP theorem", "BASE vs ACID guarantees"

V>Вот один способ решения проблемы на примере банка: http://blog.sapiensworks.com/post/2016/07/23/DDD-Eventual-Consistency

Статья вредная. Аффтар по верхам чего то там нахватался. Вот сходу — есть валом реализаций CQRS без eventual consistency. Это ортогональные понятия.

V>С двумя покупателями проблемы нет, оба успешно купят товар, а запись в бд не их проблема, проблема с третьим и четвёртым покупателями. Один из них купит то, чего уже нет. Это нормальная ситуация в магазине. Тут надо просто выяснить у бизнеса, что делать с покупателем — увеличить срок доставки, вернуть деньги, что-то ещё. И запрограммировать именно то, чего ждёт бизнес, ублажение сервера бд всегда можно отложить на потом.

Проблема в том, в 9 сценариях из 10 можно проблемы избежать вообще в принципе — транзакционность решает.
И только в тех случаях, когда масштабы бизнеса или же невозможность интегрировать системы транзакционно, не позволяют делать нормальные человеческие ACID транзакции — надо полностью осмысленно, понимая все трейдофы, идти в сторону распределенных систем и eventual consistency.
Это — боль, и для бизнеса, и для пользователей, и для девелоперов, на которую надо соглашаться когда по другому никак.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.