At work, I turned into developing an outdated enterprise application that is still in production and has stalled for the past few months due to poor design and instability.
So, we started using EF5 and applied some design templates / layers to our application.
What I'm trying to understand is what exactly should the service level do in our case? Will it be excessive architectural design or will it give some advantages without adding unnecessary complexity?
We show you what we have:
- we introduced EF (Code First with POCOs) to map our old database (works pretty well).
- we have created repositories for most of the things we need in our new data layer (concrete implementations, I see no benefit in sharing concerns using shared repositories).
Now, in the specific case, we are talking about calculating prices for an article - either by getting the price directly from the arc or from the group in which the article is located (if the price is not specified). This becomes much more difficult, because there are also different price lists (depending on the total cost of the order) and depending on the client, who may also have special prices, etc.
So my main question is: who is responsible for getting the right price?
My thoughts: The order should know about the objects of which it consists. These items, on the other hand, must know what their price is, but the order does not need to know how to calculate the price of the goods, just so that it sums up its costs.
Exclude my code for now:
ArticlePrice (POCO, mappings will soon be replaced by the 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; } }
Commodity repository:
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 ( smells like horrible code ... )
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"); }
My last questions: How to simulate my problem? Is it right that I am in the way of reorienting my code? How does my service level look right? Would I rather have ArticlePriceService, ArticleGroupPriceService etc.? But who will put these things together and calculate the right price? Will this, for example, be responsible for the OrderItemService that the GetPrice method has? But then again, orderItemService should know about other services.
Please try to provide me with possible solutions regarding the architecture, and which object / layer is doing something.
Feel free to ask me additional questions if you need more information!