Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ServiceStack, where to place business logic?

I am having a problem with the class that derives from Service, which is part of the ServiceStack library. If I setup a separate class that derives from Service and place the Get or Any methods within then everything runs fine, however, the problem is that that class itself does not have reference to any of the business logic. It is fine as long as I return static data but if I want to integrate it into the business logic then that is not working. I want the Get method to be part of the same class in which my business logic lies. Is that bad design and if what can I do to get around it? The error I am getting is that the class that derives from Service gets instantiated for whatever reason (which according to my current understanding makes very little sense to me). Should the class, deriving from Service, not just sort out the routing logic?

Here some code to illustrate my problem:

This code runs fine but the problem is that class DTO knows nothing about content of the class ClassWithBusinessLogic:

public class ClassWithBusinessLogic
{
    public ClassWithBusinessLogic()
    {
        string hostAddress = "http://localhost:1337/";

        WebServiceHost host = new WebServiceHost("MattHost", new List<Assembly>() { typeof(DTOs).Assembly });
        host.StartWebService(hostAddress);
        Console.WriteLine("Host started listening....");

        Console.ReadKey();
    }
}

public class HelloWorldRequest : IReturn<string>
{
    public string FirstWord { get; set; }

    public HelloWorldRequest(string firstWord)
    {
        FirstWord = firstWord;
    }
}

public class DTO : Service
{
    public string Get(HelloWorldRequest request)
    {
        return request.FirstWord + " World!!!";
    }
}

Now, the following is actually what I want but the code behaves unexpected, essentially it is not working:

public class ClassWithBusinessLogic : Service
{
    private string SomeBusinessLogic { get; set; }

    public ClassWithBusinessLogic()
    {
        string hostAddress = "http://localhost:1337/";

        //Simplistic business logic
        SomeBusinessLogic = "Hello";

        WebServiceHost host = new WebServiceHost("MyHost", new List<Assembly>() { typeof(DTO).Assembly });
        host.StartWebService(hostAddress);
        Console.WriteLine("Host started listening....");

        Console.ReadKey();
    }

    public string Get(HelloWorldRequest request)
    {
        return SomeBusinessLogic;
    }
}

public class HelloWorldRequest : IReturn<string>
{
    public string FirstWord { get; set; }

    public HelloWorldRequest(string firstWord)
    {
        FirstWord = firstWord;
    }
}

In order to run the following classes are needed as well:

public class WebServiceHost : AppHostHttpListenerBase
{
    public WebServiceHost(string hostName, List<Assembly> assembliesWithServices) : base(hostName, assembliesWithServices.ToArray())
    {
        base.Init();
    }

    public override void Configure(Funq.Container container)
    { }

    public void StartWebService(string hostAddress)
    {
        base.Start(hostAddress);
    }

    public void StopWebService()
    {
        base.Stop();
    }
}

public class WebServiceClient
{
    private JsonServiceClient Client { get; set; }

    public WebServiceClient(string hostAddress)
    {
        Client = new JsonServiceClient(hostAddress);
    }

    public ResponseType Get<ResponseType>(dynamic request)
    {
        return Client.Get<ResponseType>(request);
    }

    public void GetAsync<ResponseType>(dynamic request, Action<ResponseType> callback, Action<ResponseType, Exception> onError)
    {
        Client.GetAsync<ResponseType>(request, callback, onError);
    }
}

class Client_Entry
{
    static void Main(string[] args)
    {
        Client client = new Client();
    }
}

public class Client
{

    public Client()
    {
        Console.WriteLine("This is the web client. Press key to start requests");
        Console.ReadKey();

        string hostAddress = "http://localhost:1337/";
        WebServiceClient client = new WebServiceClient(hostAddress);

        var result = client.Get<string>(new HelloWorldRequest("Hello"));
        Console.WriteLine("Result: " + result);

        Console.WriteLine("Finished all requests. Press key to quit");
        Console.ReadKey();
    }

    private void OnResponse(HelloWorldRequest response)
    {
        Console.WriteLine(response.FirstWord);
    }

    private void OnError(HelloWorldRequest response, Exception exception)
    {
        Console.WriteLine("Error. Exception Message : " + exception.Message);
    }

}
like image 447
Matt Avatar asked Oct 22 '22 09:10

Matt


1 Answers

The way your second code block reads shows that your Service Host and your Service are the same object. Where I see

public ClassWithBusinessLogic()
{
    string hostAddress = "http://localhost:1337/";

    //Simplistic business logic
    SomeBusinessLogic = "Hello";

    WebServiceHost host = new WebServiceHost("MyHost", new List<Assembly>() { typeof(DTO).Assembly });
    host.StartWebService(hostAddress);
    Console.WriteLine("Host started listening....");

    Console.ReadKey();
}

That constructor then instantiates the ServiceStack WebServiceHost, passing in its own assembly - this leads me to believe that ServiceStack will then re-initialize ClassWithBusinessLogic() since it inherits from Service, potentially going down an infinite loop.

You need to separate out your concerns - your Web Service Host, your Web Service, and your Business Logic class are all separate. Co-mingling them the way you're doing is only going to lead you to frustration. Separate them into their own classes. Your business logic can be passed into your web service via the IoC container.

So you'd end up with something like:

class BusinessLogicEngine : IBusinessLogic
---> Define your business logic interface and implementation, and all required business object entities

class WebService : Service
   constructor(IBusinessLogic)
---> Purely handles mapping incoming requests and outgoing responses to/from your business object entities. Recommend using AutoMapper (http://automapper.org/) 
---> Should also handle returning error codes to the client so as not to expose sensitive info (strack traces, IPs, etc)


class WebServiceHost
---> Constructor initializes the WebServiceHost with the typeof(WebService) from above.
like image 171
Mike Pugh Avatar answered Oct 27 '22 09:10

Mike Pugh