Question

I am trying to wrap my head around ServiceStack and utilizing it to expose RESTful services.

I am currently using a MVC/Service/Repository/UnitOfWork type pattern where a basic operation to get customers might look like this:

MVC Controller Action --> Service Method --> Repository --> SQL Server

My questions are:

  1. What do my SS services return? Domain objects? Or do I return a DTO that has a collection of customers? And if so, what are the customers? Domain objects or view models or ??
  2. Should the SS services replace my service layer?
  3. Am I taking the totally wrong approach here?

I guess I am a little confused how to make all of this live side-by-side.

Domain Object

public class Customer
{
    public int Id {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
}

View Model

public class CustomerViewModel
{
    public int Id {get;set;}
    public string FirstName {get;set;}
    ....
}

Controller

public class CustomersController : Controller
{
    ICustomerService customerService;

    public CustomersController(ICustomerService customerService)
    {
        this.customerService = customerService;
    }

    public ActionResult Search(SearchViewModel model)
    {
        var model = new CustomersViewModel() {
            Customers = customerService.GetCustomersByLastName(model.LastName); // AutoMap these domain objects to a view model here
        };

        return View(model);
    }
}

Service

public class CustomerService : ICustomerService
{
    IRepository<Customer> customerRepo;

    public CustomerService(IRepository<Customer> customerRepo)
    {
        this.customerRepo = customerRepo;
    }

    public IEnumerable<Customer> GetCustomersByLastName(string lastName)
    {
        return customerRepo.Query().Where(x => x.LastName.StartsWith(lastName));
    }
}
Was it helpful?

Solution

First of all, and this is just a personal preference, I would get rid of your repository layer and just access/validate your data directly from the service operation(s). There's no point having all these extra layers if all you're doing is passing parameters along.

In answer to your questions:

1) Your service(s) should return DTO(s) (source), you mention you're using an MVC application so make sure you make use of the IReturn interface on your operations, this will allow you to do something like var customers = client.Get(new GetCustomers()); in your controller action, see here. How you use that DTO is upto you, you can use it as a ViewModel if you wish or create a seperate ViewModel should you require further properties from other sources.

2) Yes, ServiceStack IS the service layer in your application, typically you would have all your interaction going through this layer, there is no need for all these different layers (My first point above) for the sake of it, it seem's as if the architecture of this application is way more complicated then it needs to be..

3) I would think so yes, you seem to be over thinking your application, cut out all these layers

In terms of your example above and following the recommendation here. I would do something like this:

Project Structure (These can be folders within one project or separated into different projects dependant on how big your application is:)

Although for small projects with only a few services it's ok for everything to be in a single project and to simply grow your architecture when and as needed.

- SamProject.Web
    App_Start
        AppHost.cs
    Controllers
        CustomerController.cs

- SamProject.ServiceInterface 
    Services
        CustomersService.cs
    Translators                 // Mappings from Domain Object > DTO
        CustomersTranslator.cs

- SamProject.Data               // Assumes using EF
    CustomersContext.cs
    Customer.cs

- SamProject.ServiceModel     
    Operations               
        CustomersService            
            GetCustomers.cs
            GetCustomer.cs
            CreateCustomer.cs           
    Resources
        CustomerDTO.cs

Code

DTO:

public class CustomerDTO
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Operation:

[Route("/customers/{Id}")]
public class GetCustomer : IReturn<CustomerDTO>
{
    public int Id { get; set; }
}

[Route("/customers")]
public class GetCustomers : IReturn<IEnumerable<CustomerDTO>>
{
    public string LastName { get; set; }
}

Service:

public class CustomersService : Service
{
    private readonly CustomerContext _dbCustomerContext;

    public CustomersService(CustomerContext dbCustomerContext)
    {
        _dbCustomerContext = dbCustomerContext;
    }

    public object Get(GetCustomer request)
    {
        return _dbCustomerContext.Customers
               .FirstOrDefault(c => c.Id == request.Id)
               .Select(c => c.Translate());
    }

    public object Get(GetCustomers request)
    {
        if (string.IsNullOrEmpty(request.LastName))
        {
            return _dbCustomerContext.Customers.ToList()
                   .Select(c => c.Translate());
        }

        return _dbCustomerContext.Customers
               .Where(c => c.LastName == request.LastName).ToList()
               .Select(c => c.Translate());
    }
}

Controller:

public class CustomersController : Controller
{
    private readonly JsonServiceClient _client;

    public CustomersController(JsonServiceClient client)
    {
        _client = client;
    }

    public ActionResult Search(SearchViewModel model)
    {
        var customers = _client.Get(new GetCustomers
        {
            LastName = model.LastName
        });

        return View(customers);
    }

}

Notes

  • I like to think of DTO(s) as Resources
  • I like to separate out my code into a structured folder structure based on the intent of the code, this just helps later on when the code base becomes a lot bigger but on small to medium sized applications you probably wouldn't need to separate your code out like this
  • I haven't talked about other features such as logging, validation, IoC (just showing concrete implementations) etc..
  • Translate is an extension method which is held in Translators, alternatively you could use the built in translator
  • In ServiceModel I do have a clearly defined folder structure, however, the namespace of my service operations is just SamProject.ServiceModel not SamProject.ServiceModel.Operations.CustomersService. My resources, however, are in the SamProject.ServiceModel.Resources namespace.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top