Skip to content

Commit ba4fa2c

Browse files
jonathanlabcharlesvien
authored andcommitted
refactor: properly use inversify
I was doing some dumb stuff with .get<T> instead of just injecting the services.
1 parent 4e174dd commit ba4fa2c

File tree

8 files changed

+79
-51
lines changed

8 files changed

+79
-51
lines changed

ARCHITECTURE.md

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,56 @@ export const MAIN_TOKENS = Object.freeze({
6767
});
6868
```
6969

70-
### Using a Service
70+
### Injecting Dependencies
71+
72+
Services should declare dependencies via constructor injection:
73+
74+
```typescript
75+
import { inject, injectable } from "inversify";
76+
import { MAIN_TOKENS } from "../di/tokens";
77+
78+
@injectable()
79+
export class MyService {
80+
constructor(
81+
@inject(MAIN_TOKENS.OtherService)
82+
private readonly otherService: OtherService,
83+
) {}
84+
85+
doSomething() {
86+
return this.otherService.getData();
87+
}
88+
}
89+
```
90+
91+
### Using Services in tRPC Routers
92+
93+
tRPC routers resolve services from the container:
7194

7295
```typescript
73-
import { get } from "@main/di"; // or @renderer/di
96+
import { container } from "../../di/container";
97+
import { MAIN_TOKENS } from "../../di/tokens";
98+
99+
const getService = () => container.get<MyService>(MAIN_TOKENS.MyService);
74100

75-
const myService = get<MyService>(TOKENS.MyService);
76-
myService.doSomething();
101+
export const myRouter = router({
102+
getData: publicProcedure.query(() => getService().getData()),
103+
});
104+
```
105+
106+
### Testing with Mocks
107+
108+
Constructor injection makes testing straightforward:
109+
110+
```typescript
111+
// Direct instantiation with mock
112+
const mockOtherService = { getData: vi.fn().mockReturnValue("test") };
113+
const service = new MyService(mockOtherService as OtherService);
114+
115+
// Or rebind in container for integration tests
116+
container.snapshot();
117+
container.rebind(MAIN_TOKENS.OtherService).toConstantValue(mockOtherService);
118+
// ... run tests ...
119+
container.restore();
77120
```
78121

79122
## IPC via tRPC
@@ -85,26 +128,22 @@ We use [tRPC](https://trpc.io/) with [trpc-electron](https://github.com/jsonnull
85128
```typescript
86129
// src/main/trpc/routers/my-router.ts
87130
import { z } from "zod";
131+
import { container } from "../../di/container";
132+
import { MAIN_TOKENS } from "../../di/tokens";
88133
import { router, publicProcedure } from "../trpc";
89-
import { get } from "@main/di";
90-
import { MAIN_TOKENS } from "@main/di/tokens";
134+
135+
const getService = () => container.get<MyService>(MAIN_TOKENS.MyService);
91136

92137
export const myRouter = router({
93138
// Query - for read operations
94139
getData: publicProcedure
95140
.input(z.object({ id: z.string() }))
96-
.query(async ({ input }) => {
97-
const service = get<MyService>(MAIN_TOKENS.MyService);
98-
return service.getData(input.id);
99-
}),
141+
.query(({ input }) => getService().getData(input.id)),
100142

101143
// Mutation - for write operations
102144
updateData: publicProcedure
103145
.input(z.object({ id: z.string(), value: z.string() }))
104-
.mutation(async ({ input }) => {
105-
const service = get<MyService>(MAIN_TOKENS.MyService);
106-
return service.updateData(input.id, input.value);
107-
}),
146+
.mutation(({ input }) => getService().updateData(input.id, input.value)),
108147
});
109148
```
110149

apps/array/src/main/di/container.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,10 @@ import { ExternalAppsService } from "../services/external-apps/service.js";
55
import { GitService } from "../services/git/service.js";
66
import { MAIN_TOKENS } from "./tokens.js";
77

8-
/**
9-
* Main process dependency injection container
10-
*/
118
export const container = new Container({
129
defaultScope: "Singleton",
1310
});
1411

15-
// Bind services
16-
container
17-
.bind<ContextMenuService>(MAIN_TOKENS.ContextMenuService)
18-
.to(ContextMenuService);
19-
container
20-
.bind<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService)
21-
.to(ExternalAppsService);
22-
container.bind<GitService>(MAIN_TOKENS.GitService).to(GitService);
23-
24-
export function get<T>(token: symbol): T {
25-
return container.get<T>(token);
26-
}
12+
container.bind(MAIN_TOKENS.ExternalAppsService).to(ExternalAppsService);
13+
container.bind(MAIN_TOKENS.GitService).to(GitService);
14+
container.bind(MAIN_TOKENS.ContextMenuService).to(ContextMenuService);

apps/array/src/main/di/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { container, get } from "./container.js";
1+
export { container } from "./container.js";
22
export { MAIN_TOKENS } from "./tokens.js";

apps/array/src/main/services/context-menu/service.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {
44
type MenuItemConstructorOptions,
55
nativeImage,
66
} from "electron";
7-
import { injectable } from "inversify";
7+
import { inject, injectable } from "inversify";
88
import type { DetectedApplication } from "../../../shared/types.js";
9-
import { get } from "../../di/container.js";
109
import { MAIN_TOKENS } from "../../di/tokens.js";
1110
import { getMainWindow } from "../../trpc/context.js";
1211
import type { ExternalAppsService } from "../external-apps/service.js";
@@ -35,15 +34,15 @@ import type {
3534
export class ContextMenuService {
3635
private readonly ICON_SIZE = 16;
3736

38-
private getExternalAppsService() {
39-
return get<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService);
40-
}
37+
constructor(
38+
@inject(MAIN_TOKENS.ExternalAppsService)
39+
private readonly externalAppsService: ExternalAppsService,
40+
) {}
4141

4242
private async getExternalAppsData() {
43-
const service = this.getExternalAppsService();
4443
const [apps, lastUsed] = await Promise.all([
45-
service.getDetectedApps(),
46-
service.getLastUsed(),
44+
this.externalAppsService.getDetectedApps(),
45+
this.externalAppsService.getLastUsed(),
4746
]);
4847
return { apps, lastUsedAppId: lastUsed.lastUsedApp };
4948
}

apps/array/src/main/trpc/routers/context-menu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { z } from "zod";
2-
import { get } from "../../di/container.js";
2+
import { container } from "../../di/container.js";
33
import { MAIN_TOKENS } from "../../di/tokens.js";
44
import type { ContextMenuService } from "../../services/context-menu/service.js";
55
import { publicProcedure, router } from "../trpc.js";
66

77
const getService = () =>
8-
get<ContextMenuService>(MAIN_TOKENS.ContextMenuService);
8+
container.get<ContextMenuService>(MAIN_TOKENS.ContextMenuService);
99

1010
export const contextMenuRouter = router({
1111
showTaskContextMenu: publicProcedure

apps/array/src/main/trpc/routers/external-apps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { z } from "zod";
2-
import { get } from "../../di/container.js";
2+
import { container } from "../../di/container.js";
33
import { MAIN_TOKENS } from "../../di/tokens.js";
44
import type { ExternalAppsService } from "../../services/external-apps/service.js";
55
import { publicProcedure, router } from "../trpc.js";
66

77
const getService = () =>
8-
get<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService);
8+
container.get<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService);
99

1010
export const externalAppsRouter = router({
1111
getDetectedApps: publicProcedure.query(() => getService().getDetectedApps()),
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import { z } from "zod";
2-
import { get } from "@/main/di/container.js";
3-
import { MAIN_TOKENS } from "@/main/di/tokens.js";
4-
import type { GitService } from "@/main/services/git/service.js";
2+
import { container } from "../../di/container.js";
3+
import { MAIN_TOKENS } from "../../di/tokens.js";
4+
import type { GitService } from "../../services/git/service.js";
55
import { publicProcedure, router } from "../trpc.js";
66

7+
const getService = () => container.get<GitService>(MAIN_TOKENS.GitService);
8+
79
export const gitRouter = router({
810
detectRepo: publicProcedure
911
.input(z.object({ directoryPath: z.string() }))
10-
.query(async ({ input }) => {
12+
.query(({ input }) => {
1113
if (!input.directoryPath) return null;
12-
13-
const gitService = get<GitService>(MAIN_TOKENS.GitService);
14-
if (!gitService) return null;
15-
16-
return gitService.detectRepo(input.directoryPath);
14+
return getService().detectRepo(input.directoryPath);
1715
}),
1816
});

biome.jsonc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
"javascript": {
4444
"formatter": {
4545
"quoteStyle": "double"
46+
},
47+
"parser": {
48+
// Required for InversifyJS @inject() parameter decorators
49+
"unsafeParameterDecoratorsEnabled": true
4650
}
4751
},
4852
"assist": {

0 commit comments

Comments
 (0)