As far as I understand it a Service is more like a controller. its a web layer.
Is it suppose to be Per database entity?
Can I define custom functions and router to it with route variables?
Where can I find documentation about IRepository, how to implement and best practices?
A ServiceStack Service is the implementation of a Web Service or HTTP API (it does support other use-cases but I’ digress). MVC Controllers is the C in the MVC pattern typically used to display server generated HTML UIs.
Services have no correlation with a DB Entity, the Service should just be designed to accomplish what it needs to do. I wouldn’t try imposing a forced API design around rules like DB entities, you should have the freedom to accomplish each API as you see fit.
It sounds like you’re seeking a blanket guidance for how to develop every Service which I’m vehemently opposed of and recommend avoiding blanket rules, I’d always create the simplest implementation then gradually refactor for DRY or for better cohesive & logical code structure as the implementation grows.
Have a look at this basic Customer REST Example for how to implement a simple CRUD example with OrmLite and ServiceStack.
For a more real world example with validation have a look at the implementation for World Validation which shows how to implement the same UI in 10 different UI technologies & approaches (all using the same back-end implementation).
I’d implement your simple Post example with something like:
//DB Model
public class Post {
[AutoIncrement] //assuming an auto id
public int Id { get; set; }
// all other post props...
}
//Request DTO
public class CreatePost : IReturn<CreatePostResponse>
{
// Post properties you want users to be able to modify
}
public class CreatePostResponse
{
public int Id { get; set; } //can also return `Post Result` if you prefer
public ResponseStatus ResponseStatus { get; set; }
}
//Service Implementation
public class PostServices : Service
{
public object Any(CreatePost request)
{
var post = request.ConvertTo<Post>(); //map matching props to DB Model
// anything else you want new posts to be created with...
var id = Db.Insert(post, selectIdentity:true);
return new CreatePostResponse {
Id = id
};
}
}
If you want an an CreatePostAndNotify aggregate service later that calls multiple Service have a look at using the Service Gateway for how to invoke other Services from within a Service.
Ok. Im starting to digest it.
Just a quick question about the subject.
[ServiceStack.Route("/web/post/{ResearchId}", "POST")]
public class SocialPostRequest
{
public long ResearchId { get; set; }
public SocialPost Data { get; set; }
}
public class SocialPost {
public string Content {get; set; }
}
I want to be able to send in my request the json:
{
"Content" : "test"
}
and not
{
"SocialPost" : {
"Content" : "test"
}
}
Is it possible?
Thanks for your detailed answers. Appreceated.
But I want to re-use SocialPost in different use-cases. I don’t think it is a good idea to duplicate this class over and over.
I think it is better to create wrappers such as SocialPostRequest. these wrappers can be different between requests.
The Data object its self, the SocialPost will be the same one.
DTOs have 2 primary purposes to define your public Service contract and to dictate the wire format, you’re essentially violating both for some misguided motivation on internal code reuse. Please read DRY vs Intent, DTO properties are used to declaratively define your Service Contract, they’re not implementation logic that should be manipulated for code-reuse, they should be used to explicitly define your Services Contract.
You can’t do both, either you have a top-level property or you don’t. For external APIs it’s better to have a flat structure which makes the API more accessible and interoperable so I would ditch your unnecessary nested class.
IMO you’re falling into the bad habit of prematurely forcing unnecessary complexity to follow some preconceived design patterns, make sure you know exactly the value that the added complexity is bringing and that it’s actually adding value where ever you’re using it. How you want to structure your code base is up to you, however you have to live with the consequences of your choices.
Somehow Im getting the feeling that this is overengeneering…
Once you rename a property and you forget to do that in ALL the DTOs the .Convert<> wont work anymore and you created a bug…
this is my opinion about that. I think its too granular.
Thanks!
More importantly renaming a DTO property would break your public Service Contract, something you shouldn’t be doing if you have already external clients consuming your API.
I’d say your priorities are misaligned, your Service Layer is your most important contract which should be designed top-down so that it captures the ideal API contract for the Service that it’s providing, they shouldn’t be dictated by some internal defensive coding strategy of the day that you perceive would best prevent future refactoring bugs.
If you really want to, your DTOs and Data Models can share the same interfaces where renaming its properties would rename all implementing types or you can avoid using AutoMapping and copy the properties explicitly, but IMO it’s unnecessary, if you’re going to be refactoring your public DTO service contracts you should be diligent in ensuring that any refactoring is implemented property and not introducing breaking changes, if they’re not already covered by tests.
In my case, this is an internal API for a REACT app.
Do you think it is a good practice in a closed api use-case to use the database entity and not the dto’s?
Thanks
Hi,
I have migrated all of my old controller to servicestack. and autoquery.
For some reason all the GET reqeusts are not invoking:
[Authenticate]
[ServiceStack.Route("/api/research/graph/{ResearchGraphId}", "GET")]
public class ResearchGraphRequest {
public string ResearchGraphId { get; set; }
}
public class ResearchGraphService
{
public IGraphResearch GraphResearch { get; set; }
public async Task<object> Any(ResearchGraphRequest researchGraphRequest)
{
var res = await GraphResearch.GetAllRelated(researchGraphRequest.ResearchGraphId);
return res;
}
}