How to send params of current request to the constructor of the service? - nestjs

Update: I have fixed it by only supplying the pattern of the path of the collection, and creating a function that can parse the provided IDs and now the functions themselves create the collections when they are called upon and it also works with Typescript:)
Updated in the repository:
https://github.com/Darkbound/nestjs-firebase/tree/main/src/firebase
In the user service:
https://github.com/Darkbound/nestjs-firebase/blob/main/src/user/user.service.ts
In the purchase transactions service: https://github.com/Darkbound/nestjs-firebase/blob/main/src/user/modules/purchase-transaction/purchase-transaction.service.ts
In the purchase transactions controller: https://github.com/Darkbound/nestjs-firebase/blob/main/src/user/modules/purchase-transaction/purchase-transaction.controller.ts#L14
Now the functionality works out of the box, the service class simply needs to extend the FirebaseCollectionService and give it the pattern of the path to the collection and thats it!
https://github.com/Darkbound/nestjs-firebase I have uploaded it into a repository, you only need to add .env with the keys for firebase admin.
And the specific example: https://github.com/Darkbound/nestjs-firebase/blob/main/src/user/modules/purchase-transaction/purchase-transaction.service.ts
I have created a class that gives me the functionality to perform CRUD operations on firebase, so that I can just directly inherit from it for any of my CRUD resources, as the logic is again usually mostly the same. Just like Nestjs generator gives me all of the routes for it.
#Injectable()
export class UserService extends NestjsFirebase<User> {
constructor(#InjectFirebaseAdmin() firebase: FirebaseAdmin) {
super(firebase, "users");
// console.log(userId);
}
}
This works great, I can reuse that for any level 1 collection I have in firebase, however if I want to get into a nested collection on firebase, well thats a problem, because the path there needs to be dynamic and super(firebase, "this is no longer just users").
Say if I want to access the transactions of a user, so users/SomeUserIdXYZ/transactions, then the path is entirely dependent on the userId and is changing, therefor, I need to recreate the instance of the service (I simply need a new instance of the class), with a new path:
super(firebase, ["users", userId, "transactions"]
However with my still limited knowledge about Nestjs I know that everything in it basically is a Singleton and there is probably no way to do this? To get a new instance of the service, for every request that I have?
The solution that I can think of is, to handle that within my route functions, so if its a findTransactions:
#Get("users/:userId/transactions")
async findTransactions(#Param("userId") userId: string) {
return this.userService.findAll(`users/${userId}/transactions`);
}
And I am pretty sure that this will work, if I add a path argument to each of the functions, but this seems like coupling the Controller with what my Path in firebase should look like, instead I need to be able to give it just the params so that it can create its own path.
This is NestjsFirebase:
#Injectable()
class NestjsFirebase<T> {
constructor(#InjectFirebaseAdmin() private readonly firebase: FirebaseAdmin, private readonly collectionPath: string) {}
async findAll(userId: string): Promise<T> {
const db = new FirebaseCollectionService<T>(this.firebase, this.collectionPath);
return await db.findAll(userId);
}
}
export class FirebaseCollectionService<T> {
protected db: CollectionReference<T>;
constructor(firebase: FirebaseAdmin, collectionPath: string) {
super(firebase.db);
this.db = this.createCollectionPath(collectionPath);
}
public async findAll(id: string) {
... some logic to find all transactions ...
}
}

Related

What type of data should be passed to domain events?

I've been struggling with this for a few days now, and I'm still not clear on the correct approach. I've seen many examples online, but each one does it differently. The options I see are:
Pass only primitive values
Pass the complete model
Pass new instances of value objects that refer to changes in the domain/model
Create a specific DTO/object for each event with the data.
This is what I am currently doing, but it doesn't convince me. The example is in PHP, but I think it's perfectly understandable.
MyModel.php
class MyModel {
//...
private MediaId $id;
private Thumbnails $thumbnails;
private File $file;
//...
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id->asString(),
[
'name' => $this->file->name(),
'thumbnails' => $this->thumbnails->toArray(),
]
)
);
}
}
MediaDeleted.php
final class MediaDeleted extends AbstractDomainEvent
{
public function name(): string
{
return $this->payload()['name'];
}
/**
* #return array<ThumbnailArray>
*/
public function thumbnails(): array
{
return $this->payload()['thumbnails'];
}
}
As you can see, I am passing the ID as a string, the filename as a string, and an array of the Thumbnail value object's properties to the MediaDeleted event.
How do you see it? What type of data is preferable to pass to domain events?
Updated
The answer of #pgorecki has convinced me, so I will put an example to confirm if this way is correct, in order not to change too much.
It would now look like this.
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id,
new MediaDeletedEventPayload($this->file->copy(), $this->thumbnail->copy())
)
);
}
I'll explain a bit:
The ID of the aggregate is still outside the DTO, because MediaDeleted extends an abstract class that needs the ID parameter, so now the only thing I'm changing is the $payload array for the MediaDeletedEventPayload DTO, to this DTO I'm passing a copy of the value objects related to the change in the domain, in this way I'm passing objects in a reliable way and not having strange behaviours if I pass the same instance.
What do you think about it?
A domain event is simply a data-holding structure or class (DTO), with all the information related to what just happened in the domain, and no logic. So I'd say Create a specific DTO/object for each event with the data. is the best choice. Why don't you start with the less is more approach? - think about the consumers of the event, and what data might they need.
Also, being able to serialize and deserialize the event objects is a good practice, since you could want to send them via a message broker (although this relates more to integration events than domain events).

DDD : Business Logic which need infra layer access should be in application service layer, domain service or domain objects?

For an attribute which need to be validated, lets say for an entity we have country field as VO
This country field needs to be validated to be alpha-3 code as per some business logic required by domain expert.
NOTE:
*We need to persist this country data as it can have other values also and possible in future there can be addition, updating and deleting of the country persisted data.
This is just one example using country code which may rarely change, there can be other fields which needs to be validated from persistence like validating some quantity with wrt data in persistence and it won't be efficient to store them in memory or prefetching them all.
Another valid example can be user creation with unique and valid domain email check, which will need uniqueness check from persistence
*
Case 1.
Doing validation in application layer:
If we call repository countryRepo.getCountryByCountryAlpha3Code() in application layer and then if the value is correct and valid part of system we can then pass the createValidEntity() and if not then can throw the error directly in application layer use-case.
Issue:
This validation will be repeated in multiple use-case if same validation need to be checked in other use-cases if its application layer concern
Here the business logic is now a part of application service layer
Case 2
Validating the country code in its value object class or domain service in Domain Layer
Doing this will keep business logic inside domain layer and also won't violate DRY principle.
import { ValueObject } from '#shared/core/domain/ValueObject';
import { Result } from '#shared/core/Result';
import { Utils } from '#shared/utils/Utils';
interface CountryAlpha3CodeProps {
value: string;
}
export class CountryAlpha3Code extends ValueObject<CountryAlpha3CodeProps> {
// Case Insensitive String. Only printable ASCII allowed. (Non-printable characters like: Carriage returns, Tabs, Line breaks, etc are not allowed)
get value(): string {
return this.props.value;
}
private constructor(props: CountryAlpha3CodeProps) {
super(props);
}
public static create(value: string): Result<CountryAlpha3Code> {
return Result.ok<CountryAlpha3Code>(new CountryAlpha3Code({ value: value }));
}
}
Is it good to call the repository from inside domain layer (Service
or VO (not recommended) ) then dependency flow will change?
If we trigger event how to make it synchronous?
What are some better ways to solve this?
export default class UseCaseClass implements IUseCaseInterface {
constructor(private readonly _repo: IRepo, private readonly countryCodeRepo: ICountryCodeRepo) {}
async execute(request: dto): Promise<dtoResponse> {
const someOtherKeyorError = KeyEntity.create(request.someOtherDtoKey);
const countryOrError = CountryAlpha3Code.create(request.country);
const dtoResult = Result.combine([
someOtherKeyorError, countryOrError
]);
if (dtoResult.isFailure) {
return left(Result.fail<void>(dtoResult.error)) as dtoResponse;
}
try {
// -> Here we are just calling the repo
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
const dataOrError = MyEntity.create({...request,
key: someOtherKeyorError.city.getValue(),
country: countryOrError.getValue(),
});
const commandResult = await this._repo.save(dataOrError.getValue());
return right(Result.ok<any>(commandResult));
} catch (err: any) {
return left(new AppError.UnexpectedError(err)) as dtoResponse;
}
}
}
In above application layer,
this part of code :
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
it it right to call the countryCodeRepo and fetch result or this part should be moved to domain service and then check the validity of the countryCode VO?
UPDATE:
After exploring I found this article by Vladimir Khorikov which seems close to what I was looking, he is following
As per his thoughts some domain logic leakage is fine, but I feel it will still keep the value object validation in invalid state if some other use case call without knowing that persistence check is necessary for that particular VO/entity creation.
I am still confused for the right approach
In my opinion, the conversion from String to ValueObject does not belong to the Business Logic at all. The Business Logic has a public contract that is invoked from the outside (API layer or presentation layer maybe). The contract should already expect Value Objects, not raw strings. Therefore, whoever is calling the business logic has to figure out how to obtain those Value Objects.
Regarding the implementation of the Country Code value object, I would question if it is really necessary to load the country codes from the database. The list of country codes very rarely changes. The way I've solved this in the past is simply hardcoding the list of country codes inside the value object itself.
Sample code in pseudo-C#, but you should get the point:
public class CountryCode : ValueObject
{
// Static definitions to be used in code like:
// var myCountry = CountryCode.France;
public static readonly CountryCode France = new CountryCode("FRA");
public static readonly CountryCode China = new CountryCode("CHN");
[...]
public static AllCountries = new [] {
France, China, ...
}
public string ThreeLetterCode { get; }
private CountryCode(string threeLetterCountryCode)
{
ThreeLetterCode = threeLetterCountryCode;
}
public static CountryCode Parse(string code)
{
[...] handle nulls, empties, etc
var exists = AllCountries.FirstOrDefault(c=>c.ThreeLetterCode==code);
if(exists == null)
// throw error
return exists;
}
}
Following this approach, you can make a very useful and developer-friendly CountryCode value object. In my actual solution, I had both the 2 and 3-letter codes and display names in English only for logging purposes (for presentation purposes, the presentation layer can look up the translation based on the code).
If loading the country codes from the DB is valuable for your scenario, it's still very likely that the list changes very rarely, so you could for example load a static list in the value object itself at application start up and then refresh it periodically if the application runs for very long.

Loopback.js tests with Mocha initializing repositories with references to other repositories

I'm fairly new to loopback and Mocha, only having done some minor Node development previously
I'm trying to write some simple tests following official examples, but when I try to write the helper to start tests with an empty database I get stuck as I don't know how to initialize my repositories.
I have a Customer repository constructor defined like:
constructor(
#inject('datasources.database') dataSource: DatabaseDataSource, #repository.getter('CustomerRepository') protected customerRepositoryGetter: Getter<CustomerRepository>,
) {
super(Account, dataSource);
this.customer = this.createBelongsToAccessorFor('customer', customerRepositoryGetter,);
this.registerInclusionResolver('customer', this.customer.inclusionResolver);
}
The customer model has a one-to-many relationship with an account model, which have a close to identical looking constructor.
My problem is in a test helper function I export the following function and I do not know how to write the second argument for the 2 repositories:
export async function givenEmptyDatabase() {
await new AccountRepository(testdb).deleteAll();
await new CustomerRepository(testdb).deleteAll();
}

Mapping Entity-to-DTO (and vice-versa) in Nest.js

I'm building an API with Nest.js and I've been using a mapper to convert the TypeORM entity to a DTO (and vice-versa).
Until now, I've been doing this manually:
public static async entityToDto(entity: UserEntity): Promise<UserDto> {
const dto = new UserDto();
dto.id = entity.id;
dto.emailAddress = entity.emailAddress;
dto.firstName = entity.firstName;
dto.lastName = entity.lastName;
dto.addressLine1 = entity.addressLine1;
dto.addressLine2 = entity.addressLine2;
dto.townCity = entity.townCity;
[...]
return dto;
}
In my opinion, this is a nice (albeit inflexible) approach. It explicitly controls which fields are returned to the user, minimizing the chance of leaking sensitive fields (like password hash). However, I was under the impression that the purpose of a DTO is to have a single place to modify data about something. If I needed to add a field, I'd have to modify both the DTO and the mapper.
It seems to be the convention to have one mapper per entity. However, if I don't want to return, for example, the accountStatus field, I would have to write a new mapper. So I have now multiple mappers which would need to be modified.
I had the idea to write a "universal" mapper which looks at the fields in the DTO, and maps them to the fields in the entity.
I'm relatively new to TypeScript and Nest.js, so I was wondering how others manage this.
I suggest you should try object property map built-in by typescript. Basically, your entity can be map to dto based on the similar property name like below
public static async entityToDto(entity: UserEntity): Promise<UserDto> {
const dto : UserDTO = ({
...entity,
additionalProperty: entity.someProperty
});
return dto;
}
Any property that sharing the same name between DTO and Entity will be mapped. It is far more clean and more flexible.

Use Firestore collection get() return type in a function

I'm currently trying to write some code that retrieves a collection from my Firestore instance.
My codebase uses the service repository pattern to keep business logic seperate from the code that retrieves data. For this reason I've made the following code:
import { injectable, inject } from "inversify";
import { IOfficeRepository, TYPES } from "../common/types";
import { Firestore } from "#google-cloud/firestore";
#injectable()
export default class OfficeRepository implements IOfficeRepository {
private fireStoreClient: Firestore;
constructor(#inject(TYPES.FireStoreFactory) firestoreFactory: () => Firestore) {
this.fireStoreClient = firestoreFactory();
};
public async getOffice(officeId: string): Promise<FirebaseFirestore.QueryDocumentSnapshot<FirebaseFirestore.DocumentData>> {
const officeCollection = "offices";
const document = await this.fireStoreClient.collection(officeCollection).get();
return document;
};
}
What I'd like to do is return the value from the get() call to my service, in the service I will be performing checks and executing the business logic that I need.
The get() returns a Promise<FirebaseFirestore.QuerySnapshot<FirebaseFirestore.DocumentData>>, but I am unable to use this as a return type for the function in my repository. I just get the following error:
Type 'QuerySnapshot' is missing the following properties from type 'QueryDocumentSnapshot': createTime, updateTime, data, exists, and 3 more.
I've already looked-up the error, but I wasn't able to find any solution or a post where someone was trying to return the result from the get() function before performing any logic on the result.
So my question is: How would I be able to make this setup work? Or is there something I am doing wrong with this setup? If so, what would be another approach to work this out while using the service repository pattern?
Your declared return type of QueryDocumentSnapshot doesn't match the actual return type of QuerySnapshot.
This line of code:
const document = await this.fireStoreClient.collection(officeCollection).get();
performs a query for all of the documents in the officeCollection collection. As you can see from the API documentation, CollectionReference.get() yields a QuerySnapshot object. The entire set of documents will be available in the returned docs property.
It seems that you expect getOffice to return a single document instead. I'm noticing that you never used the argument officeId to narrow down your query to just the one document you want. Perhaps you meant to do something like this instead to get a single document using its ID?
const document = await this.fireStoreClient
.collection(officeCollection)
.doc(officeId)
.get();
In this case, document will be a DocumentSnapshot object.

Resources