Binding new service dynamically in application.ts or in controller - node.js

we have the following scenario with REST custom connectors.
I have followed the lb4 guidelines to create REST based custom connectors, let's say i have two connectors i.e secrets and hello-world.
I also have the loopback application which has data sources, models, repositories and services for 2 integrations.
But I want to maintain a single controller which has service injection in constructor like below
#inject(ConnectorServiceBindings.CONNECTOR_SERVICE) public externalDataService: ConnectorRepositoryService,
here ConnectorRepositoryService is an interface which has contract for method called getSomething(), so i have 2 services which implements interface for calling respective connector.
So in my application.ts i have this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass( SecretsService, ); this to bind the injection with respective service.
But i needed to have a condition here to select the binding like below
if (type === "secrets") { // how to pass this type during runtime
this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass(
SecretsManagerServiceService,
);
} else {
this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass(
HelloWorldService,
);
}
is there a way we can do this binding whenever we get new API request?????
FYI : controller has single POST end point which internally calls interface method using this.externalDataService.getData?.()!;
Question 1 : Why do you want to bind at application.ts?
Ans : In future we want to add more connectors, so doesn't want to touch the controller code every time.

Related

ServiceStack: business logic that depends on the database itself

I'm exploring ServiceStack and I'm not sure what is the best way to implement some business logic.
Using the "Bookings CRUD" example I would like to enforce the following rule:
a given Booking can only be saved (either created or updated) if the hotel has enough free rooms for the particular dates of that booking
Please note that I'm not asking how to calculate "free rooms".
What I'm asking is, from the architectural point of view, how should this be done.
For example, one way would be:
create a request DTO to query the number of configured rooms (lets call it "QueryRooms")
use the existing "QueryBookings" to query current bookings present in database
create a " : Service" class to customize the Booking Service, in order to intercept the "CreateBooking" and "UpdateBooking" requests
inside the custom methods for "CreateBooking" and "UpdateBooking", somehow get the results of "QueryRooms" and "QueryBookings", check if there are enough free rooms for the current request, and proceed only if so
This doesn't look very clean, because the service "CreateBooking" and "UpdateBooking" would depend of "QueryRooms" and "QueryBookings".
What would be an elegant and effcient solution, using ServiceStatck?
You can override AutoQuery CRUD operations with your own Service implementation using the AutoQuery DTO.
Where you can use the Service Gateway to call existing Services which you can use to perform any additional validation & modify the request DTO before executing the AutoQuery operation to implement the API, e.g:
public class MyCrudServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
public object Post(CreateBooking request)
{
var response = Gateway.Send(new QueryRooms
{
From = request.BookingStartDate,
To = request.BookingEndDate,
});
if (response.Results.Count == 0)
throw new Exception("No rooms available during those dates");
request.RoomNumber = response.Results[0].Id;
return AutoQuery.Create(request, base.Request);
}
}
Note: calling in-process Services with the Service Gateway is efficient as it calls the C# method implementation directly, i.e. without incurring any HTTP overhead.

Clean Architecture - How to handle usecase dependencies

I am refactoring one of my older applications around to using the concept of use cases "clean architecture".
I am little confused on how to leverage the common data & entity validations
for e.g. There are 2 use cases
Allow admins to import a new workflow template
Allow admins to create new workflow template
The above use cases are called from the controllers.
In both the above cases, there are some common database level validations like:
Is there already a workflow with same name ?
To handle these validations, Do I make this as separate use-case like "checkIfWorkflowWithSameNameExists()" ?
If I make a separate use case, then what options are better to call these common validations
Can one use case call another use case directly
export function importNewWorkflowTemplate(specs){
const { workflowRepository } = specs;
const exists = checkIfWorkflowWithSameNameExists()
if(exists){
//return error
}
return new (payLoad) => {
//logic
}
}
Should I be injecting the dependent use cases
export function importNewWorkflowTemplate(specs){
const { workflowRepository, checkIfWorkflowWithSameNameExists } = specs;
return new (payLoad) => {
//logic
}
}
Should the validation belong to outer layer like the controller?
What you describe - checkIfWorkflowWithSameNameExists() - is not a use case.
This is simply a method put on a domain service, such as a repository. This could be a repository method on your workflow repository such as hasWorkflowWithName(name). The repository represents a collection of aggregates and thus knows best if there is one with the same name already.
If there is more complex domain logic to check for an existing repository and then either perform some error handling or performing specific logic to create the logic you can also consider to encapsulate these steps inside a domain service method. In this case the workflow repository interface would be injected into the workflow domain service which would then again be injected into the use cases.
With that you could use the same domain service in both use cases and the use case are responsible to orchestrate the translation between the external commands and the domain service interface and the corresponding domain entities.

Why is data access tightly coupled to the Service base in ServiceStack

I'm curious why the decision was made to couple the Service base class in ServiceStack to data access (via the Db property)? With web services it is very popular to use a Data Repository pattern to fetch the raw data from the database. These data repositories can be used by many services without having to call a service class.
For example, let's say I am supporting a large retail chain that operates across the nation. There are a number of settings that will differ across all stores like tax rates. Each call to one of the web services will need these settings for domain logic. In a repository pattern I would simply create a data access class whose sole responsibility is to return these settings. However in ServiceStack I am exposing these settings as a Service (which it needs to be as well). In my service call the first thing I end up doing is newing up the Setting service and using it inside my other service. Is this the intention? Since the services return an object I have to cast the result to the typed service result.
ServiceStack convenience ADO.NET IDbConnection Db property allows you to quickly create Database driven services (i.e. the most popular kind) without the overhead and boilerplate of creating a repository if preferred. As ServiceStack Services are already testable and the DTO pattern provides a clean endpoint agnostic Web Service interface, there's often not a lot of value in wrapping and proxying "one-off" data-access into a separate repository.
But at the same time there's nothing forcing you to use the base.Db property, (which has no effect if unused). The Unit Testing Example on the wiki shows an example of using either base.Db or Repository pattern:
public class SimpleService : Service
{
public IRockstarRepository RockstarRepository { get; set; }
public List<Rockstar> Get(FindRockstars request)
{
return request.Aged.HasValue
? Db.Select<Rockstar>(q => q.Age == request.Aged.Value)
: Db.Select<Rockstar>();
}
public RockstarStatus Get(GetStatus request)
{
var rockstar = RockstarRepository.GetByLastName(request.LastName);
if (rockstar == null)
throw HttpError.NotFound("'{0}' is no Rockstar".Fmt(request.LastName));
var status = new RockstarStatus
{
Alive = RockstarRepository.IsAlive(request.LastName)
}.PopulateWith(rockstar); //Populates with matching fields
return status;
}
}
Note: Returning an object or a strong-typed DTO response like RockstarStatus have the same effect in ServiceStack, so if preferred you can return a strong typed response and avoid any casting.

How to use Custom Routes with Auto Query

Using the first example in the ServiceStack Auto Query documentation in a project structured similar to the EmailContacts sample project (i.e. separate projects for the ServiceModel and ServiceInterface), how would one register the custom route "/movies" defined by the Route attribute?
[Route("/movies")]
public class FindMovies : QueryBase<Movie>
{
public string[] Ratings { get; set; }
}
Normally, custom routes such as these can be register by passing the ServiceInterface assembly when instantiating AppHostBase:
public AppHost() : base("Email Contact Services", typeof(ContactsServices).Assembly) {}
However, the FindMovies request DTO does not have an associated service and therefore won't be included. No routes are registered.
If I pass typeof(FindMovies).Assembly instead of or in addition to typeof(ContactsServices).Assembly, then the pre-defined route will be registered (i.e. shows up in the metadata, postman, etc.) but the custom route is still not registered (i.e. does not show up in the metadata, postman, etc.).
What is the best way to register the custom route using attributes when there is no service and the ServiceModel and ServiceInterface are in separate projects?
These issues should be resolved in v4.0.24+ that's now available on MyGet.
There's a new AutoQueryFeature.LoadFromAssemblies property to specify an additional list of assemblies to scan for IQuery Request DTO's. This automatically looks in the assemblies where your other Request DTO's are defined so in most cases nothing needs to be done as it will automatically be able to find your query services.
The routes for Query DTO's should now appear on the metadata pages as well as Swagger and Postman metadata API's.

Servicestack - Grouping like services together

Was wondering if there's a recommended best-practice way of grouping similar services together in what's becoming a larger and larger project. Say that most of my services can be lumped in either dealing with "Pro" data or "Amateur" data (the data goes way beyond a simple flag in a table, the data itself is totally different, from different tables, on the pro or amateur side.
I know I can add routes to my classes...
/pro/service1
/am/service2
It looks like I can put the DTOs in namespaces....
What about the Service.Interface items (Service and Factory classes). Would you put those into namespaces also?
Finally, is there a way for the metadata page to reflect these groupings? I started to go down this road, but all the services listed out in alphabetical order, and you couldn't see the route or namespace differences between service1 and service2.
thank you
If you want, you can split multiple Service implementations across multiple dlls as described on the Modularizing Services wiki.
You can safely group service implementation classes into any nested folder groupings without having any impact to the external services. But changing the namespaces on DTO's can have an effect if your DTO's make use of object, interfaces or abstract classes which emit type info containing full namespaces.
In ServiceStack v4.09+ (now on MyGet) the MetadataFeature includes the ability to customize the ordering of the metadata page, e.g you can reverse the order of the metadata pages with:
var metadata = (MetadataFeature)Plugins.First(x => x is MetadataFeature);
metadata.IndexPageFilter = page => {
page.OperationNames.Sort((x,y) => y.CompareTo(x));
};
Organising your large project:
For a complex service(s) I setup 4 projects in one solution.
AppHost, This takes care of the configuration of the service. (References Model, Service and Types)
Model, This is the database model (Does not reference other projects)
Service, This is the implementation of the service only, not the interfaces or DTOs (References Model and Types)
Types, This includes my Interfaces, DTOs and routes. (Does not reference other projects)
Having a separate Types library allows the distribution to clients, for example for use with the ServiceStack JsonServiceClient.
Yes you can namespace the Interfaces, DTOs and factory classes, any way you want. They will work as long as they are referenced in your service correctly.
If you are trying to separate more than one service, you should consider separating your service code into logical folders within the Service project. i.e.
/Service/Pro
/Service/Amateur
Wrap the outer code of your Service methods in a public partial static class MyServiceStackApplication, with an appropriate name. Then reference this as the assembly in the AppHost constructor. So for example:
Pro Service (Service Project/Pro/UserActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Pro
{
public class UserActionsService : Service
{
public User Get(GetUserRequest request)
{
}
}
// ...
}
}
Pro Service (Service Project/Pro/OtherActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Pro
{
public class OtherActionsService : Service
{
public Other Get(GetOtherRequest request)
{
}
}
// ...
}
}
Amateur Service (Service Project/Am/UserActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Amateur
{
public class UserActionsService : Service
{
public User Get(GetUserRequest request)
{
}
}
// ...
}
}
etc.
You can see from the above code we can have multiple files, all separated out and organised, but one assembly for ServiceStack to reference in the AppHost:
public AppHost() : base("Pro & Amateur Services", typeof(MyServiceStackApplication).Assembly) {}
Using the reference to the MyServiceStackApplication assembly, and using the partial keyword allows you to organise the code into manageable groupings.
Metadata:
Unfortunately separating the metadata by namespace isn't supported. You could try and customize the MetaDataFeature yourself, but it does seem like a useful feature, being able to separate multiple services where they are hosted in the one ServiceStack application. I would suggest you raise a feature request.
Mythz is bringing out features faster than lightning. :) Seems like he has that covered in the next release and you should be able to apply a custom filter to HostContext.Metadata.OperationNamesMap.

Resources