1

In our codebase I have seen some sharing (I believe inappropriate) between different ServiceStack services. I don't think it's good idea as the "boundaries" for each services become convoluted. By "boundaries" for example, it can mean the boundary for database connection and I'm going to use database connection boundary as example to show what I mean.

For example if I have below two services.

[Route("/service-a/", Verbs = "POST")]
public class DtoServiceA : IReturn<IList<string>>
{
}

public class ServiceA : Service //service stack service
{
    public ServiceA(Funq.Container container) : base(container)
    {
        _container = container; //I didn't type the full code
    }

    public IList<string> Post(DtoServiceA request)
    {
        //do something that belongs to ServiceA
        //then resolve ServiceB and call Post() from ServiceB
        Container.Resolve<ServiceB>().Post(new DtoServiceB());

        return new List<string>();
    }
}

[Route("/service-b/", Verbs = "POST")]
public class DtoServiceB : IReturn<IList<string>>
{
}

public class ServiceB : Service //service stack service
{
    public ServiceB(Funq.Container container) : base(container)
    {
        _container = container; //I didn't type the full code
    }

    public IList<string> Post(DtoServiceB request)
    {
        //do something that belongs to ServiceB
        return new List<string>();
    }
}

Suppose if I control the database connection with in the Post methods like so

    public IList<string> Post(DtoServiceA request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //do something that belongs to ServiceA
            //then resolve ServiceB and call Post() from ServiceB
            Container.Resolve<ServiceB>().Post(new DtoServiceB());
            //above line will fail because connection has already been opend by ServiecA.Post()
        }
    }

    public IList<string> Post(DtoServiceB request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {

        }
    }

Because database connection has already been opened, so obviously this isn't a good way to "share" services. But we have a more complicated way to hand the opening of db connection basically it'll count / detect whether it's open hence won't open the connection more than once. But this is code smell to me.

I have senn somewhere else that people have suggested similar way to share services. I'm not 100% convinced this is good advice.

I would probably do something like below, and extract the code inside using statement to a separate class / classes.

    public IList<string> Post(DtoServiceA request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //move code to a searapte "none servicestack service", which
            //can be just a normal c# class
            //so that the "boundary" is being controlled at service stack level
            //and the actual code that does the job is extracted elsewhere
            Resolve<NoneServiceStackServiceA>().DoSomething();
            Resolve<NoneServiceStackServiceB>().DoSomething();
        }
    }

    public IList<string> Post(DtoServiceB request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //move code to a searapte "none servicestack service", which
            //can be just a normal c# class
            //so that the "boundary" is being controlled at service stack level
            //and the actual code that does the job is extracted elsewhere
            Resolve<NoneServiceStackServiceB>().DoSomething();
        }
    }

Any suggestions / recommendations are welcome. Thanks.

Jeff
  • 13,079
  • 23
  • 71
  • 102

1 Answers1

3

It's not a bad idea in the sense of usage of resources as opening an extra db connection is very lightweight with connection pooling (default for SqlServer).

It's fine if the calling Service is like an "aggregation service" where it's just combining the outputs of multiple services into single service response, an example like this is in Northwind CustomerDetailsService:

public class CustomerDetailsService : Service
{
    public CustomerDetailsResponse Get(CustomerDetails request)
    {
        var customer = Db.SingleById<Customer>(request.Id);
        using (var orders = base.ResolveService<OrdersService>())
        {
            var ordersResponse = orders.Get(new Orders { CustomerId = customer.Id });
            return new CustomerDetailsResponse
            {
                Customer = customer,
                CustomerOrders = ordersResponse.Results,
            };
        }
    }
}

Otherwise from a code architecture point of view it's not ideal as it blurs the dependency graph, I would prefer to pull out common functionality in a shared dependency or extension method, e.g:

ServiceA
  - SharedDep

ServiceB
  - SharedDep

Avoid Interfaces in Service Responses

On a side note I would strongly discourage the use of Interfaces in Service Responses. Collections are seldomly ever mocked and IList<T> is especially useless as it's effectively always hiding a concrete List<T>, which it needs to be converted to in order to access its useful LINQ extension methods.

Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks for the speedy response. How many hours do you sleep? :_) I guess one of the issues I had with "reusing" SSService is the serviceB would have committed / disposed the db connection by the time serviceA tries to commit. So we have a ConnectionManager which essentially is a Unit of Work which counts how many connections has been asked to open, if > 0, the code just ignores the calling of open, same for commit / dispose. But this seems like a bad idea. – Jeff Dec 18 '14 at 06:50
  • Re "avoid interfaces in service response", yes it makes perfect sense and I totally agree. But I guess it's my fingers that can always only type IList instead of List :_) – Jeff Dec 18 '14 at 06:51
  • Also I guess there's no "one architecture / pattern fits all scenarios". So I tend to use SS for what's been designed for, i.e. handling HTTP requests / restful. I try not to "pollute" the core libs by enforcing SS architecture. But this hasn't been the case in the code base I'm working on. The SS approach basically has been the only approach which worries me a bit. – Jeff Dec 18 '14 at 06:54