Seeking a more elegant way to model a User object

I've been using Effect for a bit, and the following code works, but I'm unsure if there's a better more elegant way to do it.
Not looking for solutions, just ideas. Is there a better way to model my User object (class?) and avoid the separate functions perhaps?
import * as Schema from '@effect/schema/Schema';
import * as Effect from 'effect/Effect';

import {DbRecordParseError} from '../lib/errors.server.ts';
import * as DateString from './date.server.ts';
import * as Email from './email.server.ts';
import * as Uuid from './uuid.server.ts';

const UserBrand = Symbol.for('UserBrand');
const UserIdBrand = Symbol.for('UserIdBrand');

class ParseUserIdError {
  readonly _tag = 'ParseUserIdError';
}

class ParseUserError {
  readonly _tag = 'ParseUserError';
}

export const userNameSchema = Schema.Trim.pipe(
  Schema.minLength(2, {
    message: () => 'Name must be at least 2 characters',
  }),
  Schema.maxLength(100, {
    message: () => 'Name cannot be more than 100 characters',
  })
);

export const userIdSchema = Uuid.uuidSchema.pipe(Schema.brand(UserIdBrand));

export const userSchema = Schema.struct({
  id: userIdSchema,
  name: userNameSchema,
  email: Email.emailSchema,
  emailVerified: Schema.boolean,
  createdAt: DateString.dateSchema,
  updatedAt: DateString.dateSchema,
}).pipe(Schema.brand(UserBrand));

export type User = Schema.Schema.To<typeof userSchema>;

export function parse(value: unknown) {
  return Effect.try({
    try: () => Schema.parseSync(userSchema)(value),
    catch: () => new ParseUserError(),
  });
}

export function parseId(value: unknown) {
  return Effect.try({
    try: () => Schema.parseSync(userIdSchema)(value),
    catch: () => new ParseUserIdError(),
  });
}
Was this page helpful?