C # Application Architecture - EF5 and Understanding Service Level

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"); } //etc. you'll get the point that this approach might be completely WRONG } 

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!

+4
source share
2 answers

You presented a simple script that may be sufficient for the repository itself.
Do you have more repositories?
Do you expect the application to grow and have more storage?

Having a service level that abstracts the data layer is recommended and used by most applications / examples I've seen, and the overhead is not that significant.

One of the reasons for using services may appear if you want to get data from several different repositories, and then perform some kind of aggregation / manipulation of data.
Then the service level will provide the logic of manipulation, while the consumer of services will not have to deal with several different repositories.
You should also think about situations when you want to have more than one object in one transaction (the value is more than one repository) and save changes to the database only when all update actions are successful.
This situation should involve the use of the Unit of Work Pattern and is likely to end the use of the service level to ensure proper testing of the modules.

0
source

When I started with objects and architecture, my main problem was giving a good name to classes.

It seems to me that your service should be called "ShopService" (or something similar). Then your GetByArticle method should be typed by GetPriceByArticle.

The idea of ​​changing the name of the service for something more than just the price will be more meaningful and will also solve other problems (for example, your OrderPriceService, which you are thinking about).

Maybe you can ask yourself: "What is the name of my page or window that intercept this service?" Is there only one or more? If more, what do they have in common? This can help you determine a good name for your service and, therefore, various methods of getting what they need.

Tell me more. I will be happy to help.

0
source

Source: https://habr.com/ru/post/1485993/


All Articles