По време на работа бях хвърлен в разработването на наследено корпоративно приложение, което все още е в процес на производство и е спряно през последните няколко месеца поради лош дизайн и нестабилност.
Така че започнахме да използваме EF5 и да прилагаме някои дизайнерски модели / слоеве към нашето приложение.
Това, което се мъча да разбера, е: какво точно трябва да прави Сервизният слой в нашия случай? Ще бъде ли свръхархитектуриране или ще предостави някои предимства, без да добавя ненужна сложност?
Нека ви покажем какво имаме досега:
- въведохме EF (Code First with POCOs), за да картографираме нашата наследена база данни (работи сравнително добре)
- ние създадохме хранилища за повечето неща, от които се нуждаем в нашия нов слой данни (специфични реализации, не виждам никаква полза по отношение на разделянето на загриженост, използвайки общи репозитории..)
Сега в конкретния случай става въпрос за изчисляване на цени за артикул - или чрез получаване на цена от дадена статия директно, или от групата, в която е артикулът (ако няма посочена цена). Става много по-сложно, защото има и различни ценови листи (в зависимост от пълната стойност на поръчката) и в зависимост от клиента, който също може да има специални цени и т.н.
Така че основният ми въпрос е: кой е отговорен за получаването на правилната цена?
Моите мисли са: Поръчката трябва да знае за елементите, от които се състои. Тези артикули от друга страна трябва да знаят каква е цената им, но поръчката не трябва да знае как да изчисли цената на артикула, а просто трябва да обобщи разходите им.
Извадка от моя код в момента:
ArticlePrice (POCO, картографирането скоро ще бъде заменено от Fluid API)
[Table("artikeldaten_preise")]
public class ArticlePrice : BaseEntity
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("einheit")]
public int UnitId { get; set; }
[ForeignKey("UnitId")]
public virtual Unit Unit { get; set; }
[Column("preisliste")]
public int PricelistId { get; set; }
[ForeignKey("PricelistId")]
public virtual Pricelist Pricelist { get; set; }
[Column("artikel")]
public int ArticleId { get; set; }
[ForeignKey("ArticleId")]
public virtual Article Article { get; set; }
public PriceInfo PriceInfo { get; set; }
}
Хранилище за цени на артикули:
public class ArticlePriceRepository : CarpetFiveRepository
{
public ArticlePriceRepository(CarpetFiveContext context) : base(context) {}
public IEnumerable<ArticlePrice> FindByCriteria(ArticlePriceCriteria criteria)
{
var prices = from price in DbContext.ArticlePrices
where
price.PricelistId == criteria.Pricelist.Id
&& price.ArticleId == criteria.Article.Id
&& price.UnitId == criteria.Unit.Id
&& price.Deleted == false
select price;
return prices.ToList();
}
}
public class ArticlePriceCriteria
{
public Pricelist Pricelist { get; set; }
public Article Article { get; set; }
public Unit Unit { get; set; }
public ArticlePriceCriteria(Pricelist pricelist, Article article, Unit unit)
{
Pricelist = pricelist;
Article = article;
Unit = unit;
}
}
PriceService (има ужасяваща миризма на код...)
public class PriceService
{
private PricelistRepository _pricelistRepository;
private ArticlePriceRepository _articlePriceRepository;
private PriceGroupRepository _priceGroupRepository;
public PriceService(PricelistRepository pricelistRepository, ArticlePriceRepository articlePriceRepository, PriceGroupRepository priceGroupRepository)
{
_pricelistRepository = pricelistRepository;
_articlePriceRepository = articlePriceRepository;
_priceGroupRepository = priceGroupRepository;
}
public double GetByArticle(Article article, Unit unit, double amount = 1, double orderValue = 0, DateTime dateTime = new DateTime())
{
var pricelists = _pricelistRepository.FindByDate(dateTime, orderValue);
var articlePrices = new List<ArticlePrice>();
foreach (var list in pricelists)
articlePrices.AddRange(_articlePriceRepository.FindByCriteria(new ArticlePriceCriteria(list, article, unit)));
double price = 0;
double priceDiff = 0;
foreach (var articlePrice in articlePrices)
{
switch (articlePrice.PriceInfo.Type)
{
case PriceTypes.Absolute:
price = articlePrice.PriceInfo.Price;
break;
case PriceTypes.Difference:
priceDiff = priceDiff + articlePrice.PriceInfo.Price;
break;
}
}
return (price + priceDiff) * amount;
}
public double GetByPriceGroup(PriceGroup priceGroup, Unit unit)
{
throw new NotImplementedException("not implemented yet");
}
//etc. you'll get the point that this approach might be completely WRONG
}
Последните ми въпроси са: Как правилно да моделирам проблема си? Правилно ли е, че съм на път да надархитектурирам кода си? Как би изглеждал правилно моят сервизен слой? Бих ли предпочел да имам ArticlePriceService, ArticleGroupPriceService и т.н.? Но кой ще свърже тези части и ще изчисли правилната цена? Това би ли напр. да бъде отговорност на OrderItemService, която има метод "GetPrice"? Но отново orderItemService ще трябва да знае за другите услуги..
Моля, опитайте се да ми предоставите възможни решения по отношение на архитектурата и кой обект/слой какво прави.
Чувствайте се свободни да ми задавате допълнителни въпроси, ако имате нужда от повече информация!