個人開発 MVP release 🎉
MVP release しました!!
現在v1.2.0です!
次のrelease ではserver side の refactoring を行ったものを release する予定です!
https://remeet.vercel.app/
Tech Stack
言語
- TypeScript
フレームワーク
- Next.js
UI / style
- shadcn
- tailwind
ライブラリ
- React Hook Form
- Zod
Test
- Vitest
DB / BaaS
- prisma
- Supabase
CI/CD
- GitHub Action
- Husky
hosting
- Vercel
設計思想
FRONT END
Next.js(React RSC)でfrontendを構築しています.
RSCを主軸としてCSRを最小限にする意識で実装しました.
CRUD主軸のアプリのため,状態管理ライブラリも必要なく,RSC 中心で実装が実現できています.
コンポーネント作成時に意識したこと
- shadcn を使用し,車輪の再発明を防ぐ + デザインの一貫性を保つ
- 独自のコンポーネントを作成するときは以下を意識
- shadcn を使用して 実際の振る舞いをするformの実装 (使い回しができるContactForm)
- ContactForm に対してデータや初期値を入れ,create, update Form を実装(使いまわせるし,一貫性を保てる)
ContactForm.ts
type Props = {
tags: Tag[]; //データの注入
form: UseFormReturn<CreateContactsSchema>;//データの注入
action: (payload: FormData) => void;
buttonLabel: string;//表示する文字の注入
isDisabled: boolean;//送信状態の注入
isOpenLinkFields?: boolean;//データの注入
};
export const ContactForm = ({
tags,
form,
action,
isDisabled,
buttonLabel,
isOpenLinkFields = false,
}: Props) => {
//useState の省略...
form.setValue(
"tags",
selectedFormTag.map((st) => {
return st.id;
}),
{ shouldValidate: true },
);
setSelectTags([...selectTags, t]);
const filteredContactsTags = userTags.filter((c) => t.id !== c.id);
setUserTags([...filteredContactsTags]);
setTagQuery("");
};
//handler の省略....
return (CreateContactForm.tsx
type Props = {
meetupId: string;
tags: Tag[];
};
export const CreateContactForm = ({ meetupId, tags }: Props) => {
const createContactsWithMeetupId = createContacts.bind(null, meetupId);
const [state, action, isPending] = useActionState(
createContactsWithMeetupId,
null,
);
const form = useForm<CreateContactsSchema>({
resolver: zodResolver(createContactsFrontSchema),
defaultValues: {//省略...},
mode: "onChange",
});
//省略...
return (
<div>
<ContactForm
tags={tags}
form={form}
action={action}
buttonLabel={buttonLabel}
isDisabled={isDisabled}
/>
</div>
);
};
BACK END
Next.js による server function, server action でbackend を構築しています
MVC のでお決まりの controller(action) => service => repository を流用しています
全体を通してTest容易性のため action を薄く保つようにしています.
具体的な役割
- action:
- validation,
- user検証,
- service 呼び出し,
- redirect | error時のreturn
- service:
- business logic 呼び出し,
- repository 呼び出し
- repository:
- DBA
//action.ts
//paramの省略...
export const createMeetup = async (): Promise<ActionState<MeetupErrors>> => {
const validatedFields = createMeetupSchema.safeParse(rawFormData);
if (!validatedFields.success)
return {//return の省略...};
const user = await getUser();
if (!user)
return {//return の省略...};
const createdMeetupResult = await createMeetupService(user.id, {
meetupName: validatedFields.data.name,
scheduledAt: validatedFields.data.scheduledAt,
});
if (!createdMeetupResult.ok)
return {//return の省略...};
redirect(routes.dashboardMeetupDetail(createdMeetupResult.data.meetupId));
};AIについて
設計やコードレビュー,Testの作成,で主に使用していました.
実際の実装のほとんどは自分の手でやっています
この,AIが実装のほとんどを担っている時代にもかかわらず手で書いたのは「体系的に学ぶ」と言う目的があります.
確かにAIに作らせればある程度以上のものが出力されますし,早いです.
しかし,ジュニアエンジニアがそれをやってはどうでしょうか? 設計に対する理解は育つのでしょうか?(優秀な師が常にいてくれるのであれば話は別でしょうが)
自分自身で試行錯誤し,ボトルネックを見つけ,改善案をレビューしてもらう.
このプロセスこそ,今の私に大事なものだと考え実装にAIは使用を控えていました.