Object-oriented – Do you put business logic in your service implementation ?

designdesign-patternsdomain-driven-designobject-orientedsoa

Today at work a colleague was looking at a piece of module I had written a month ago as a reference implementation.
I had written a WCF service which a Windows service was consuming.
I have a single operation exposed as below.

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    void PerformASpecificTask(int randomNumber);
}

I have a similar interface in a separate assembly and I refer to this contract in a different assembly which implements this above interface,
similar to how one would implement any WCF service contracts.
I have the interface in a separate assembly because I can distribute the dll with the interface alone to the clients (Windows service in my case).

public class MyService : Contract.IMyService
{

    private readonly Domain.ISomeDomainNameHereDataProvider _domainDataProvider;
    private readonly Domain.ADomainService _aDomainService;

    public MyService(Domain.ISomeDomainNameHereDataProvider domainDataProvider, 
        Domain.ADomainService aDomainService)
    {
        _domainDataProvider = domainDataProvider;
        _aDomainService = aDomainService;
    }

    public void PerformASpecificTask(int randomNumber)
    {
        var myRequiredData = _domainDataProvider.GetMyRequiredData(randomNumber);
        myRequiredData.ForEach(data => _aDomainService.DoSomething(data));
        _domainDataProvider.SaveMyChanges(myRequiredData);
    }
}

Here ADomainService is a class which lives in an assembly named Domain and this class uses another WCF service internally,
the proxy of which is injeccted during its instantiation.

ISomeDomainNameHereDataProvider is a interface I have in the Domain assembly I also have another interface IDataAccess in the Domain assembly.
IDataAccess is implemented in my DAL assembly.

Implementation of `ISomeDomainNameHereDataProvider` is in the same `Domain` assembly
and an implementation of `IDataAccess` which lies in `DAL` is injected to it.
So `ISomeDomainNameHereDataProvider` makes use of `IDataAccess` to access the data or to be specific sql-server in this case.
Please note I didn't define `IDataAccess` in a separate assembly of its own, as this work was a small task and my `Domain` would be only one consuming it.

Now coming to the question, this colleague calls me and question why is your service talking to `DAL` directly and not using the `Domain` layer.
His reasoning was my implementation of an interface defined in `Domain` came from `DAL`.
He went to say your service has business logic and it violates `open-close` and `single-responsibility` prinicple,
I take pride in my work and explanned I don't violate the principles and all my classes have only one responsibility and
open-close is not a rule and does not apply in this usecase at hand.

I also tried reasoning for this requirement I have to get some data, perform some operation with the data and then save it back and
you need a place to co-ordinate all this and my service class does that and that is the responsibility of my service.
He wouldn't agree and started talking about requirements changes, etc.
I tried reasoning another solution would have been to move the code I have in my service implementation to a different class and that class would have the same code
and he wouldn't agree.
Am I missing something ?
Please let me know what you think of the above solution and events ?
I could only think, he comes from a `MVC` background and thinks of the service class akin to a 'Controller'.
`Controller` anyway would delegate the calls to a service layer and all the code I have in my service implementation would remain?

I have read this How accurate is "Business logic should be in a service, not in a model"? and would still require you opinions on my solution above.

Best Answer

As I see your friend have reasons to say that you have business logic in the web service.
These lines is business logic

var myRequiredData = _domainDataProvider.GetMyRequiredData(randomNumber);
myRequiredData.ForEach(data => _aDomainService.DoSomething(data));
_domainDataProvider.SaveMyChanges(myRequiredData);

In the code above your service need to know about how "get" data, how "handle" it and how "save" it.

Think about web service as about IO device/driver, which execute only input/output without knowing about what data is.

Web service can maybe execute some converting data before sending to internal service or before sending to "outside word"

If structure of your small application is - WebService - Domain - Data layer

Following Dependency inversion principle: - WebService depend on abstractions

interface IServiceProcessData
{
    void ProcessData(DataModel data);
}
  • Domain layer implement abstractions provided by top layer(Web Service) Domain layer define own abstractions for data processing

    interface IDataAccessService { void LoadDataById(int id); void SaveData(DataModel data); }

  • Data layer implement abstractions provided by Domain layer

Then your Web Service will look like this:

public void PerformASpecificTask(DataModel data)
{
  //Check preconditions
  _DomainService.ProcessData(data); 
}

And of course somewhere in WebService "main" method you composite all implementations together