Created
April 2, 2025 19:57
-
-
Save guiseek/efdcac98e9ced04d16f1e58cab784fe1 to your computer and use it in GitHub Desktop.
DI Skills
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Concrete<T> = new (...params: any[]) => T; | |
type Abstract<T> = abstract new (...params: any[]) => T; | |
type Factory<T> = (...params: any[]) => T; | |
type AsyncFactory<T> = (...params: any[]) => Promise<T>; | |
type Ref<T> = Concrete<T> | Abstract<T>; | |
type Use<T> = Concrete<T>; | |
interface Config<T> { | |
ref: Ref<T>; | |
use?: Use<T>; | |
dep?: Ref<any>[]; | |
} | |
const typeIs = { | |
factory<T>(value: unknown): value is Factory<T> { | |
return typeof value === "function"; | |
}, | |
asyncFactory<T>(value: unknown): value is AsyncFactory<T> { | |
return this.factory(value) && value.constructor.name === "AsyncFunction"; | |
}, | |
concrete<T>(value: unknown): value is Concrete<T> { | |
return this.factory(value) && typeof value.prototype === "object"; | |
}, | |
}; | |
const container = new Map(); | |
const relations = new Map(); | |
export function inject<T>(ref: Ref<T>): T { | |
const instance = container.get(ref); | |
if (!instance) { | |
throw `Provider ${ref.name} not found`; | |
} | |
return instance; | |
} | |
const determine = async <T>({ use, ref }: Config<T>) => { | |
const concrete = use ?? ref; | |
if (typeIs.factory(concrete)) { | |
const deps = relations.get(ref) ?? []; | |
if (typeIs.concrete(concrete)) { | |
return new concrete(...deps); | |
} | |
if (typeIs.asyncFactory(concrete)) { | |
return await concrete(...deps); | |
} | |
return concrete(...deps); | |
} | |
return concrete; | |
}; | |
const register = async <T>(config: Config<T>) => { | |
if (config.dep && config.dep.length) { | |
relations.set(config.ref, config.dep.map(inject)); | |
} | |
container.set(config.ref, await determine(config)); | |
}; | |
interface Skill { | |
id: string | |
name: string; | |
} | |
/** | |
* Camada data-access | |
* depende da camada domain | |
* implementa o contrato | |
*/ | |
class SkillRepositoryMockImpl implements SkillRepository { | |
async findAll() { | |
return new Array({ name: "Renan", id: crypto.randomUUID() }); | |
} | |
} | |
class SkillRepositoryHTTPImpl implements SkillRepository { | |
async findAll() { | |
return fetch("https://api.devparana.mx/api/skills").then((res) => | |
res.json() | |
); | |
} | |
} | |
/** | |
* Podem estar na camada domain | |
*/ | |
/** | |
* Contrato | |
*/ | |
abstract class SkillRepository { | |
abstract findAll(): Promise<Skill[]>; | |
} | |
/** | |
* Caso de uso | |
* depende de um contrato | |
*/ | |
class FindAllSkillsUseCase { | |
constructor(public repository: SkillRepository) {} | |
execute() { | |
return this.repository.findAll(); | |
} | |
} | |
await register({ | |
ref: SkillRepository, | |
use: SkillRepositoryHTTPImpl, | |
}); | |
await register({ | |
ref: FindAllSkillsUseCase, | |
dep: [SkillRepository], | |
}); | |
function Injectable() { | |
return <T>(use: Use<T>) => { | |
const ref = Object.getPrototypeOf(use) | |
register({ ref, use }).then() | |
}; | |
} | |
abstract class SkillService { | |
abstract findOne(): Promise<Skill>; | |
} | |
@Injectable() | |
class SkillServiceImpl extends SkillService { | |
async findOne() { | |
return { name: "Marques", id: crypto.randomUUID() }; | |
} | |
} | |
const repository = inject(SkillRepository); | |
console.log(repository); | |
const useCase = inject(FindAllSkillsUseCase); | |
console.log(useCase); | |
console.log(await useCase.execute()); | |
const service = inject(SkillService); | |
console.log(service); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment