effect-http icon indicating copy to clipboard operation
effect-http copied to clipboard

[QUESTION]: Adding Groups endpoints and Build Routes in a modular way

Open kattsushi opened this issue 9 months ago • 3 comments

Issue Description:

Currently, there's a challenge regarding the modularity and scalability of routes within the presentation layer of the application, particularly when employing DDD (Domain-Driven Design). The goal is to enhance the architecture to better separate concerns and facilitate maintainability and extensibility.

To address this issue, we aim to refactor the code to achieve a more modular approach, allowing for greater flexibility and abstraction in handling routes.

there's a way to build the routes in a way to be modular with a native technique with Effect or there is a bit limitation for now of effect-http to do that? this is the structure of the folders 📦src ┣ 📂data-access ┃ ┗ 📜user.repository.ts ┣ 📂domain ┃ ┣ 📂user ┃ ┃ ┗ 📜user.model.ts ┃ ┗ 📜models.ts ┣ 📂entry-points ┃ ┣ 📜routes.ts ┃ ┗ 📜server.ts ┣ 📂presentation ┃ ┗ 📂user ┃ ┃ ┗ 📜user.route.ts ┣ 📂test ┃ ┗ 📜.gitkeep ┗ 📜env.ts

in order to be scalable and preserve the modularity i tried this way per example using DDD, having three layers, first the presentation layer, the domain layer and the data-layer, the main issue is in the presentation layer i need to pull up the implementation of the route away from the main pipe to run the effect

src/entry-points/server.ts

import { findAllUsers } from "@api-gateway-app/presentation/user/user.route";
import { NodeRuntime } from "@effect/platform-node";
import { Effect, Layer, LogLevel, Logger, pipe } from "effect";
import { RouterBuilder } from "effect-http";
import { NodeServer } from "effect-http-node";
import { PrettyLogger } from "effect-log";
import { AppRouter } from "./routes";

export const debugLogger = pipe(
  PrettyLogger.layer(),
  Layer.merge(Logger.minimumLogLevel(LogLevel.All))
)

pipe(
  RouterBuilder.make(AppRouter, { docsPath: '/api', parseOptions: { errors: "all" } }),
  RouterBuilder.handle("findAllUsers", ({ query }) => findAllUsers(query)), // ==== i want to move this handle away from 
  // the pipe ( the best i can do for now is this passing the callback with the implementation but isn't the goal )
  RouterBuilder.build,
  Effect.provide(debugLogger),
  NodeServer.listen({ port: 3001 }),
  NodeRuntime.runMain
)

src/entry-points/routes.ts

// this implementation of definition of the AppRouter is Ok because from here we 
// can define from each scope int presentation layer in this case is user but can be 
// added others scopes / collections
import { userApi } from "@api-gateway-app/presentation/user/user.route";
import { Api } from "effect-http";

export const AppRouter = Api.make({
  title: "API Gateway",
  servers: [{ url: "http://localhost:3001" }],
}).pipe(Api.addGroup(userApi));

import { Criteria, getPaginatedResponse } from "@api-gateway-app/domain/models";
import { User } from "@api-gateway-app/domain/user/user.model";
import { pipe } from "effect";
import { Effect } from "effect";
import { Api, ApiGroup, Security  } from "effect-http";

// this is perfect because in the presentation layer we define the interface of the endpoint
export const userApi = pipe(
  ApiGroup.make("Users", {
    description: "All about Users",
  }),
  ApiGroup.addEndpoint(
    ApiGroup.get("findAllUsers", "/api/users").pipe(
      Api.setRequestQuery(Criteria),
      Api.setResponseBody(getPaginatedResponse(User)),
      Api.setSecurity(
        Security.bearer({ name: "mySecurity", description: "test" })
      ),
    )
  )
);

// this is my concern and the ugly part (its working though) that only can
// export the callback of the implementation but no the actual implementation definition, 
// this should be place the RouterBuilder.handle()
export const findAllUsers = (query: Criteria) => Effect.succeed({
  data: [{ name: "mike", id: JSON.stringify(query) }],
  cursor: "x",
  hasMore: false,
});

there's a way to achieve this ? i'm new on Effect and I am very interested in learn and contribute on examples with TDD, DDD and monorepos with Effect

Thank you in advance!

Best regards,

kattsushi avatar May 03 '24 03:05 kattsushi