Clean Architecture - How to handle usecase dependencies - node.js

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.

Related

Binding new service dynamically in application.ts or in controller

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.

DDD - domain service to store entity in not primary infrastructure

I am thinking about scenario in a way of Domain Driven design, where I have entity, lets say Cv (Curriculum vitae), which state is saved in database via repository.
Now I need to store part of the Cv in another system (ElasticSearch), which is crucial for whole app functionality like searching.
How to handle it? I am thinking about these 2 options:
1. Use domain service IndexCvGatewayServiceInterface (as interfaces implemented in infrastructure)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, IndexCvGatewayServiceInterface $indexService)
{
$cvRepository->update($this);
$indexService->update($this);
}
}
2. Listen to domain event (create infrastructure listener)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, EventDispatcheInterface $dispatcher)
{
$cvRepository->update($this);
$dispatcher->dispatch(new CvApprovedEvent($this));
}
}
I like option 2. because it separates logic for non state change purposes into infrastructure, but there is also concern, that we should know about searching as important part of our app.
You're facing here Write and Read model. Ideally after persist your entity/aggregate in the write model you should dispatch the uncommitted events of this entity and listing/subscribe to them to generate the projections (partials in elastic in your use case). For reference: https://github.com/jorge07/symfony-5-es-cqrs-boilerplate/blob/symfony-5/src/Infrastructure/User/ReadModel/Projections/UserProjectionFactory.php#L17
IMO, Entity should not contain the repository.

Query remote rest service from ddd aggregate

I've read about the Double Dispatch pattern, which enables to pass service interfaces into aggregate methods: https://lostechies.com/jimmybogard/2010/03/30/strengthening-your-domain-the-double-dispatch-pattern/, http://blog.jonathanoliver.com/dddd-double-dispatch/.
In my domain I have a BitbucketIntegration aggregate, which is local copy of a remote bitbucket account with some additional domain specific data. Now, I have to synchronize repositories and teams, etc.. from the cloud to be able to do business operations on them. In my first implementation I was using a service to access the Bitbucket Cloud, then set the aggregate's repositories, teams, account. This way I had a DDD mixed with Anemic Domain Model, since half of the aggregates state was set using setter-like methods from the service. With Double Dispatch I can pass e.g. a BitbucketService interface into method arguments. This way, the aggregate can protect it's invariants more, since some of the data can only be verified by connecting to the rest service (e.g. if the aggregate's accessToken, bitbucketAccount and repositories are in sync), which was the service's responsibility. One more thing that smells is that I have an accessToken field in my aggregate, which is only a technical concern.
Are there any recommended patterns for keeping a copy of a remote resource in a ddd aggregate? Also, how to keep the technical side out of it? Or was the first method with a domain service good enough?
Now the code looks something like:
class BitbucketIntegration extends Aggregate<UUID> {
accountId: BitbucketId
repos: List<Repository>
localData: ...
// ... and more
Single integrateWith(accessToken, queryService) {
var id = queryService.getAccountAsync(accessToken);
var repos = queryService.getReposAsync(accessToken);
return Single.zip(id, repos,
(i, r) -> new BitbucketIntegratedEvent(accessToken, i, r))
.onSubscribe(event -> apply(event))
}
Observable doSomeBusinessLocally(data) { ... return events; }
// this is triggered by a saga
Single pollForChanges(queryService) {
var dataFromRemote = queryService.synchronizeAsync(this.accessToken);
....
return event;
}
}
class CommandHandler {
queryService: BitbucketService
Completable handle(integrateCmd) {
aggregate = repo.get(integrateCmd.id);
return aggregate.integrateWith(integrateCmd.accessToken, queryService)
.flatMap(event -> repo.store(event));
}
}
As a side note, I only query Bitbucket.
EDIT:
Martin Fowler writes about accessing an external system, including the definition of an Anti-Corruption Layer, which translates the remote resource representation to domain types.
If you inject infrastructure services into your Aggregate (by constructor or by method invocation) then you won't have a pure domain model anymore. This includes even services that have interfaces defined in the domain layer. It affects testability and introduces a dependency on the infrastructure. It also breaks the Single responsibility principle and it forces the Aggregate to know things it does not really need to.
The solution to this is to call the service before and pass the result to the Aggregate's method (i.e. in the Application layer).

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.

Can I or Should I use a Global variable in Angularjs to store a logged in user?

I'm new to angular and developing my first 'real' application. I'm trying to build a calendar/scheduling app ( source code can all be seen on github ) and I want to be able to change the content if there is a user logged in (i.e. display details relevant to them) but here's the catch:
I don't want the app to be dependent on having a logged in user ( needs to be something that can be configured to work publicly, privately or both)
I don't want to implement the user/login within this app if it can be avoided ( I want to eventually include my app in another app where this might be implemented but isn't necessarily implemented using any particular security frameworks or limited to any)
I had an idea of creating some global variable user that could be referenced through out my application, or if I had to implement a system to do it all in this app that I could do so in in some abstract way so that different options could be injected in.
some of my ideas or understanding of what I should be doing may be completely wrong and ignorant of fundamentals but I genuinely do not know what approach I should take to do this.
In case it is relevant I currently don't have any back-end but eventually hope use MongoDB for storage and nodejs for services but I also want to try keep it open-ended to allow others to use different storage/backends such as sql and php
is there away to have a global uservariable/service that I could inject/populate from another (parent?) app?
If so what would be the best approach to do so?
If Not, why and what approach should I take and why?
Update
I Believe from comments online and some suggestion made to me that a service would be the best option BUT How would I go about injecting from a parent application into this applications service?
If your (single) page is rendered dynamically by the server and the server knows if you are logged-in or not, then you could do the following:
Dynamically render a script tag that produces:
<script>
window.user = { id: 1234, name: 'User A', isLoggedIn: true };
</script>
For non logged-in users:
<script>
window.user = { isLoggedIn: false };
</script>
For convinience, copy user to a value inside angular's IOC:
angular.module('myApp').value('user', window.user);
Then, you can use it in DI:
angular.module('myApp').factory('myService', function(user) {
return {
doSomething: function() {
if (user.isLoggedIn) {
...
} else {
...
}
}
};
});
Something tricky (which you should thing twice before doing [SEE COMMENTS]) is extending the $scope:
angular.module('myApp').config(function($provide) {
$provide.decorator('$controller', function($delegate, user) {
return function(constructor, locals) {
locals.$scope._user = user;
return $delegate(constructor, locals);
};
});
});
This piece of code decorates the $controller service (responsible for contructing controllers) and basically says that $scope objects prior to being passed to controllers, will be enhanced with the _user property.
Having it automatically $scoped means that you can directly use it any view, anywhere:
<div ng-if="_user.isLoggedIn">Content only for logged-in users</div>
This is something risky since you may end up running into naming conflicts with the original $scope API or properties that you add in your controllers.
It goes without saying that these stuff run solely in the client and they can be easily tampered. Your server-side code should always check the user and return the correct data subset or accept the right actions.
Yes you can do it in $rootScope. However, I believe it's better practice to put it inside a service. Services are singletons meaning they maintain the same state throughout the application and as such are prefect for storing things like a user object. Using a "user" service instead of $rootScope is just better organization in my opinion. Although technically you can achieve the same results, generally speaking you don't want to over-populate your $rootScope with functionality.
You can have a global user object inside the $rootScope and have it injected in all your controllers by simply putting it into the arguments of the controller, just as you do with $scope. Then you can implement functionalities in a simple check: if($rootScope.user). This allows you to model the user object in any way you want and where you want, acting as a global variable, inside of Angular's domain and good practices with DI.
Just to add on my comment and your edit. Here is what the code would look like if you wanted to be able to re-use your user service and insert it into other apps.
angular.module('user', []).service('userService', [function(){
//declare your user properties and methods
}])
angular.module('myApp', ['user'])
.controller('myCtrl', ['userService', '$scope', function(userService, scope){
// you can access userService from here
}])
Not sure if that's what you wanted but likewise you could have your "user" module have a dependency to another "parent" module and access that module's data the same way.

Resources