Implementing Dependency Injection for AuthService with UserService in TypeScript

New to effect. I have a UserService that I am using to create a UserServiceLive: Layer.Layer<UserService, never, never> and I have an AuthService that I want to use to create a AuthServiceLive: Layer.Layer<AuthService, never, UserService> that depends on the UserService

My implementation is as follows
export class UserService extends Context.Tag("@jnd/UserService")<
  UserService,
  {
    readonly getUserById: (
      userId: string,
    ) => Effect.Effect<User, UserNotFoundError | DrizzleQueryError, never>;
    readonly getAccountByUsername: (
      username: string,
    ) => Effect.Effect<
      Account,
      AccountNotFoundError | DrizzleQueryError,
      never
    >;
    readonly getUserByUsername: (
      username: string,
    ) => Effect.Effect<
      User,
      UserNotFoundError | AccountNotFoundError | DrizzleQueryError,
      never
    >;
  }
>() {}

export const UserServiceLive = Layer.succeed(
  UserService,
  UserService.of({
    getUserById: getUserById,
    getAccountByUsername: getAccountByUsername,
    getUserByUsername: getUserByUsername,
  }),
);

export class AuthService extends Context.Tag("@jnd/AuthService")<
AuthService,
{
  readonly signInWithUsername: (input: {
    username: string;
    password: string;
  }) => Effect.Effect<void, any, UserService>;
}
>() {}


In a seperate file I have an implementation for signInWithUsername on the AuthService

export function signInWithUsername(input: {
  username: string;
  password: string;
}): Effect.Effect<
  never,
  UserNotFoundError
  | DrizzleQueryError
  | AccountNotFoundError
  | PasswordHashingError,
  UserService
> {
return Effect.gen(function* (_) { ..... }
}

I am currently creating the AuthServiceLive layer as follows
import { signInWithUsername as signInWithUsernameImpl } from "~/server/auth/signInWithUsername";
export const AuthServiceLive: Layer.Layer<AuthService, never, UserService> =
  Layer.effect(
    AuthService,
    Effect.gen(function* (_) {
      const userService = yield* UserService;

      const signInWithUsername = (input: {
        username: string;
        password: string;
      }) =>
        Effect.provide(
          signInWithUsernameImpl(input),
          Layer.succeed(UserService, userService),
        );

      return AuthService.of({
        signInWithUsername,
      });
    }),
  );

This is returning the correct type of Layer.Layer<AuthService, never, UserService> however I feel like this might not be the *correct * way to provide the requirements.

I saw in the docs the example given is
class Logger extends Context.Tag("Logger")<
  Logger,
  { readonly log: (message: string) => Effect.Effect<void> }
>() {}
 
const LoggerLive = Layer.effect(
  Logger,
  Effect.gen(function* () {
    const config = yield* Config
    return {
      log: (message) =>
        Effect.gen(function* () {
          const { logLevel } = yield* config.getConfig
          console.log(`[${logLevel}] ${message}`)
        })
    }
  })
)

but this requires an in line definition of the function. Is there a certain way I should be providing the layers requirements instead?
Was this page helpful?