8

I'm trying to switch to clean architecture from a traditional layered architecture approach and trying to figure out where to put business logic. Please consider the below scenario -

Employee class (exists in Efcore db first scaffolded in core)-

class Employee {
 public int Id {get; set;}
 public int TimeWorked {get; set;}
 public int ZoneCode {get; set;}
 public datetime JoiningDate {get; set;}
 public decimal Reimbursement {get; set;}
}
class SomeMasterData { //Could be a value object
 public int ZoneCode {get; set;}
 public decimal OvertimeAllowedForZoneCode {get; set;}
}
class SomeOtherMasterData { //Could be a value object
 public int OvertimeMultiplier {get; set;} //and whatever other props
}

Business logic related to the employee -

int CalculateReimbursement(Employee emp, SomeMasterData masterData, , SomeOtherMasterData masterData2) {
 // Use data from Employee (fields like zone code, joining date etc) and
 // data from SomeMasterData to calculate the reimbursement amount for the employee
 // The below business logic is random, created for this question
 if(emp.ZoneCode == masterData.ZoneCode && emp.JoiningDate > 'some date') {
 return masterData.OvertimeAllowedForZoneCode * masterData2.OvertimeMultiplier * emp.TimeWorked;
 }
 else //More business logic based on many entities and value objects
 ...
}

My question is - Considering Clean Architecture, where to put the CalculateReimbursement method? Below are a few options -

  1. In domain services in the core - But is this what domain services are for?
  2. In the Employee class - But that class is autogenerated by Efcore db first scaffolding so I can't change it. Should I create another Employee partial class with this method?
  3. In some sort of "helper" class within the core - if so, what do I call it?
  4. Somewhere else that makes more sense keeping Clean Architecture in mind?
asked Apr 16, 2019 at 7:53
8
  • where to put the CalculateReimbursement method? Why it should be a method? Why not a class within the core? Commented Apr 17, 2019 at 7:16
  • As a use case? Should that class go in a Use Case folder? Also, are we talking about a class with only this one method? Commented Apr 17, 2019 at 10:31
  • Some operations can be domain's classes, not necessarily use cases. Use cases are business-specific operations (high-level policies); CalculateReimbursement could be a low-level policy within the core. Not everything within code is Value Objects or entities, there could be also functions modelled as classes. Commented Apr 17, 2019 at 11:55
  • EF uses partial classes. You can create another partial class with the same name as the entity, and put your logic there. Clean Architecture probably precludes this approach, however. Commented Apr 17, 2019 at 22:01
  • 1
    Why would calculation of overtime be the responsibility of an employee? In a non digital system you may ask an employee to record their hours somewhere and another accounting type employee would calculate the amount of overtime to be paid. In the digital world you should follow a similar pattern. Commented Apr 18, 2019 at 7:53

4 Answers 4

7

In addition to Mike's answer and in line with the comments above.

The business knowledge encapsulated in CalculateReimbursement fits in the core. It seems to be an Entity.

Entities encapsulate Enterprise-wide business rules. An entity can be an object with methods, or it can be a set of data structures and functions. It doesn’t matter so long as the entities could be used by many different applications

Clean Code Blog : The Clean Architecture - R.C.Martin

Emphasis of mine.

The author doesn't say if these functions are services, helpers or utilities because the focus is not on the implementation details, it's on the boundary: business-specific rules belong to the core.

In relation to the previous, the author stresses the idea of isolating core from implementation details such as frameworks or infrastructure.

We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

Clean Code Blog : The Clean Architecture - R.C.Martin

Q: But then we're talking about creating a duplicate class for each entity. Isn't that too much work when the autogenerated entities already exist?

Yes and no.

In the first place, nothing forces us to map entities 1:1. Domain and persistence data models are different abstractions. One entity of the domain might need N persistence entities or vice-versa. Bear in mind that each model is designed to be written and read efficiently in their respective stores (memory | disk).

In the second place, part of the work is executed by the auto-generator. Isn't it? That's the only point of using such tools. Then, our efforts must be aimed at modelling the core, not the persistence. We change the persistence when the core so require it. We make the persistence work for us, not the other way around.1

Last but not least, segregation and isolation are basic notions of clean architectures. Statements such like -less LoC make code easier and cheaper to maintain- are fallacious. Good abstractions and well-segregated concerns make code easier and cheaper to maintain.

Q: Also, extra work for manually keeping the domain version of the entities keeping in sync with any changes in the DB tables?

This's a wrong conclusion based on a wrong assumption: business depends on persistence. That's not how clean architecture works.

In clean architectures, the persistence cannot dictate how to model the domain, because the domain doesn't depend on persistence. It's just the opposite. At this point, I would suggest reading about stable dependency principle because it's the bedrock of this architectural style.

Q: I understand that having a domain version of the models is ideal but just wondering if there's a solution where the autogenerated entities can be reused to reduce the manual work while also following clean architecture as closely as possible?

Matter of convenience.

To my experience, implementing well-defined architectural styles partially doesn't worth the efforts, overall when the argument for it is -autogenerated code can fulfil my needs for persistence and business logic and save me a handful of LoCs at the same time-.2

Why? Because mid-implementations lead us to nobody's land. A place where the advantages of the architecture are half-truths and half-lies at the same time. In this limbo, the LoC saving to which we appealed turns against us because it prevents us from achieving the supposed advantages of the architecture.


1: Data-centric domain models are not bad per se. Some applications have very little business logic|rules (or none at all). The most suitable approach depends on the complexity of the business requirements. For example, using the domain model for a query-and-report process is unnecessarily complicated. In cases like this, it helps to recognize simplicity for what it is, get rid of the domain layers, and gain direct access to the infrastructure layer.

2: One-fit-all solutions don't exist

answered Apr 17, 2019 at 19:48
7

You could use the models created by EF as purely models made for persisting your data. You should map these to an actual Employee domain model class that can hold all your business logic and that would be truly your domain models.

answered Apr 16, 2019 at 7:56
2
  • Thank you. But then we're talking about creating a duplicate class for each entity. Isn't that too much work when the autogenerated entities already exist? Also, extra work for manually keeping the domain version of the entities keeping in sync with any changes in the db tables? I understand that having a domain version of the models is ideal but just wondering if there's a solution where the autogenerated entities can be reused to reduce the manual work while also following clean architecture as closely as possible? Commented Apr 17, 2019 at 10:30
  • You can use generated entities as a domain models as far as you are happy to have your domain logic to be coupled to the database structure. Commented Jun 20, 2021 at 11:10
1

Business logic maybe always handles several entities, and should not be in the layer of generated entities.

Ideally it should be its own layer in the core. I know one case where there was formalized, versioned Word documentation describing business rules, and in the code every piece of busines code referred to the documentation, where the version number somewhat congrues with the change request ticket.

The classes of the business logic, BOs (Business Objects), are in there own layer in the core, modularized.

The frontend, GUI & controllers, determine what is needed, and what is the best form, hence one must ensure that a developer can dive into the layers' depth and still have an overview.

This means that for every use-case on every level/layer, there should be recognition, short package paths, not a too complex prescribed subhierarchy, but a clean separation of other use-cases (without code copying as standard means).

Additional artifacts, intermediate "pass-on" layers of code (as with interfaces) should be thin as possible. Best no boiler plate code. Personally I like interfaces; separation of concerns might lead to implement a component against an interface, whereas the effective class implements much more.

answered Apr 18, 2019 at 10:16
0

I guess the good answer is, it depends.

Domain Put core domain logic in the Domain layer to ensure the integrity of the entity (i.e. Root Aggregate) Application logic should not reside here.

Application This layer for the most part, acts as a coordinator across multiple domain root entities as well as non domain entities or services. Some developers tend to put too much of this in a domain service layer, which is wrong. Domain Aggregates are very well isolated business rules for a given root aggregate (entity). Domain services shouldn't worry about concerns of the other domain services. They are isolated and should remain that way. Application logic (uses cases) are specific to the specific line of business and is responsible to know which domain service to invoke to achieve a specific business use case.

answered Jun 2, 2021 at 17:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.