ā§Ļā§Ē āĻŽā§‡, ⧍ā§Ļ⧍ā§Ģ¡5 āĻŽāĻŋāύāĻŋāϟ āĻĒāĻĄāĻŧāϤ⧇

āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ Go āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ Go āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ CRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ

āĻĒāĻžāĻ āĻ•ā§āϤāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰāϝ⧋āĻ—ā§āϝ Go āĻœā§‡āύ⧇āϰāĻŋāĻ• CRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻļāĻŋāϖ⧁āύ: āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰāϝ⧋āĻ—ā§āϝ List/Get/Create/Update/Delete āĻĢā§āϞ⧋, āĻ¸ā§āĻĒāĻˇā§āϟ āĻ•āύāĻ¸ā§āĻŸā§āϰ⧇āχāĻ¨ā§āϟ, reflection āĻ›āĻžāĻĄāĻŧāĻž āĻāĻŦāĻ‚ āĻĒāĻĄāĻŧāϤ⧇ āϏāĻšāϜ āϕ⧋āĻĄāĨ¤

āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ Go āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ Go āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ CRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ

āϕ⧇āύ Go-āϤ⧇ CRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻāĻžāĻŽā§‡āϞāĻžāϝāĻŧ āĻĒāĻĄāĻŧ⧇\n\nCRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻļ⧁āϰ⧁āϤ⧇ āϏāĻžāϧāĻžāϰāĻŖ āĻšāϝāĻŧāĨ¤ āφāĻĒāύāĻŋ āϞāĻŋāϖ⧇āύ GetUser, āĻĒāϰ⧇ ListUsers, āϤāĻžāϰāĻĒāϰ Orders, āϤāĻžāϰāĻĒāϰ InvoicesāĨ¤ āĻ•āϝāĻŧ⧇āĻ•āϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻĒāϰ⧇ āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰ āύāĻ•āϞ āĻ•āĻĒāĻŋāϰ āϗ⧁āĻšā§āĻ› āĻšāϝāĻŧ⧇ āϝāĻžāϝāĻŧ āϝ⧇āĻ–āĻžāύ⧇ āϛ⧋āϟ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϗ⧁āϞ⧋ āĻšāĻžāϰāĻŋāϝāĻŧ⧇ āϝāĻžāϝāĻŧāĨ¤\n\nāϏāĻŦāĻšā§‡āϝāĻŧ⧇ āĻŦ⧇āĻļāĻŋ āϝ⧇āϟāĻž āĻŦāĻžāϰāĻŦāĻžāϰ āĻšāϝāĻŧ āϤāĻž SQL āύāϝāĻŧāĨ¤ āϏ⧇āϟāĻž āĻšāĻšā§āϛ⧇ āϚāĻžāϰāĻĒāĻžāĻļ⧇āϰ āĻĒā§āϰāĻŦāĻžāĻš: āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāύ⧋, āϰ⧋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāĻž, “not found” āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāĻž, āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻāϰāϰāϗ⧁āϞ⧋ āĻŽā§āϝāĻžāĻĒ āĻ•āϰāĻž, pagination āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰāĻž, āĻāĻŦāĻ‚ āχāύāĻĒ⧁āϟāϗ⧁āϞ⧋ āϏāĻ āĻŋāĻ• āϟāĻžāχāĻĒ⧇ āĻ•āύāĻ­āĻžāĻ°ā§āϟ āĻ•āϰāĻžāĨ¤\n\nāĻĒāϰāĻŋāϚāĻŋāϤ āĻšāϟāĻ¸ā§āĻĒāϟāϗ⧁āϞ⧋: āĻ•āĻĒāĻŋāĻ•ā§ƒāϤ Scan āϕ⧋āĻĄ, context.Context āĻāĻŦāĻ‚ āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ⧇āϰ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ, boilerplate LIMIT/OFFSET āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻŋāĻ‚ (āĻ•āĻ–āύ⧋ āĻ•āĻ–āύ⧋ total count-āϏāĻš), “0 rows means not found” āĻšā§‡āĻ•, āĻāĻŦāĻ‚ āĻ•āĻĒāĻŋ-āĻĒ⧇āĻ¸ā§āϟ āĻ•āϰāĻž INSERT ... RETURNING id āĻ­ā§āϝāĻžāϰāĻŋāϝāĻŧ⧇āĻļāύāĨ¤\n\nāϝāĻ–āύ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ•ā§āώāϤāĻŋāϤāĻ•āϰ āĻšāϝāĻŧ⧇ āĻ“āϠ⧇, āĻ…āύ⧇āĻ• āϟāĻŋāĻŽ reflection-āĻ āĻšāĻžāϤ āĻŦāĻžāĻĄāĻŧāĻžāϝāĻŧāĨ¤ āĻāϟāĻŋ “āĻāĻ•āĻŦāĻžāϰ āϞāĻŋāϖ⧁āĻ¨â€ CRUD-āĻāϰ āĻĒā§āϰāϤāĻŋāĻļā§āϰ⧁āϤāĻŋ āĻĻ⧇āϝāĻŧ: āϝ⧇ āϕ⧋āύ⧋ struct āύāĻŋāύ āĻāĻŦāĻ‚ āĻ•āϞāĻžāĻŽāϗ⧁āϞ⧋ āϰuntime-āĻ āĻ­āϰāĻŋāϝāĻŧ⧇ āĻĻāĻŋāύāĨ¤ āĻ–āϰāϚ āĻĒāϰ⧇ āĻĻ⧇āĻ–āĻž āϝāĻžāϝāĻŧāĨ¤ reflection-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āϕ⧋āĻĄ āĻĒāĻĄāĻŧāϤ⧇ āĻ•āĻ āĻŋāύ, IDE āϏāĻŽāĻ°ā§āĻĨāύ āĻ–āĻžāϰāĻžāĻĒ āĻšāϝāĻŧ, āĻāĻŦāĻ‚ āĻŦā§āϝāĻ°ā§āĻĨāϤāĻž compile time āĻĨ⧇āϕ⧇ runtime-āĻ āϚāϞ⧇ āϝāĻžāϝāĻŧāĨ¤ āĻĢāĻŋāĻ˛ā§āĻĄā§‡āϰ āύāĻžāĻŽ āĻŦāĻĻāϞāĻžāύ⧋ āĻŦāĻž nullable āĻ•āϞāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰāĻž āϛ⧋āϟ āĻŦāĻĻāϞāϗ⧁āϞ⧋ āϕ⧇āĻŦāϞ āĻŸā§‡āĻ¸ā§āϟ āĻŦāĻž āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ⧇ āφāĻļā§āϚāĻ°ā§āϝāϤāĻž āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤\n\nāϟāĻžāχāĻĒ-āϏ⧇āĻĢ āĻĒ⧁āύāσāĻŦā§āϝāĻŦāĻšāĻžāϰ āĻŽāĻžāύ⧇ āĻšāϞ⧋ CRUD āĻĢā§āϞ⧋ āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āĻ•āĻŋāĻ¨ā§āϤ⧁ Go-āĻāϰ āĻĒā§āϰāϤāĻŋāĻĻāĻŋāύ⧇āϰ āϏ⧁āĻŦāĻŋāϧāĻžāϗ⧁āϞ⧋ āĻšāĻžāϰāĻžāύ⧋ āύāϝāĻŧ: āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āϏāĻŋāĻ—āύ⧇āϚāĻžāϰ, āĻ•āĻŽā§āĻĒāĻžāχāϞāĻžāϰ-āĻšā§‡āĻ•āĻĄ āϟāĻžāχāĻĒ, āĻāĻŦāĻ‚ autocomplete āϝāĻž āϏāĻ¤ā§āϝāĻŋāχ āϏāĻšāĻžāϝāĻŧāĻ•āĨ¤ āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āĻĻāĻŋāϝāĻŧ⧇ āφāĻĒāύāĻŋ Get[T] āĻ“ List[T] āĻŽāϤ⧋ āĻ…āĻĒāĻžāϰ⧇āĻļāύ reuse āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ, āĻāĻ•āχ āϏāĻŽāϝāĻŧ⧇ āĻĒā§āϰāĻ¤ā§āϝ⧇āĻ• āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻĨ⧇āϕ⧇ āϝ⧇ āĻ…āĻ‚āĻļāϗ⧁āϞ⧋ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϝāĻžāϝāĻŧ āύāĻž (āϝ⧇āĻŽāύ āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āĻāĻ•āϟāĻŋ āϰ⧋ T-āϤ⧇ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāϤ⧇ āĻšāϝāĻŧ) āϏ⧇āϗ⧁āϞ⧋ āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āĻĻāĻžāĻŦāĻŋ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤\n\nāĻāχ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύāϟāĻŋ āχāĻšā§āĻ›āĻžāĻ•ā§ƒāϤāĻ­āĻžāĻŦ⧇ āĻĄā§‡āϟāĻž āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇āĨ¤ āĻāϟāĻŋ SQL āĻ“ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻ•āύāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϟ āĻ“ āύāĻŋāĻ¸ā§āϤ⧇āϜ āϰāĻžāϖ⧇āĨ¤ āĻāϟāĻŋ āφāĻĒāύāĻžāϰ āĻĄā§‹āĻŽā§‡āχāύ āĻŽāĻĄā§‡āϞ āĻ•āϰāĻžāϰ āĻŦāĻž āĻŦāĻŋāϜāύ⧇āϏ āϰ⧁āϞ āφāϰ⧋āĻĒ āĻ•āϰāĻžāϰ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰ⧇ āύāĻž, āĻ•āĻŋāĻ‚āĻŦāĻž āϏāĻžāĻ°ā§āĻ­āĻŋāϏ-āϞ⧇āϭ⧇āϞ āϞāϜāĻŋāĻ• āĻĒā§āϰāϤāĻŋāĻ¸ā§āĻĨāĻžāĻĒāύ āĻ•āϰ⧇ āύāĻžāĨ¤\n\n## āĻĄāĻŋāϜāĻžāχāύ āϞāĻ•ā§āĻˇā§āϝ (āĻ“ āĻ•āĻŋ āĻŦāĻŋāώāϝāĻŧāϏāĻŽā§‚āĻš āĻāĻ–āĻžāύ⧇ āϏāĻŽāĻžāϧāĻžāύ āĻšāĻŦ⧇ āύāĻž)\n\nāĻāĻ•āϟāĻŋ āĻ­āĻžāϞ⧋ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻĒā§āϰāϤāĻŋāĻĻāĻŋāύ⧇āϰ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏāϕ⧇ āĻĒā§‚āĻ°ā§āĻŦāĻžāύ⧁āĻŽāĻžāύāϝ⧋āĻ—ā§āϝ āĻ•āϰ⧇ āϤ⧋āϞ⧇āĨ¤ āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒāĻĄāĻŧāϞ⧇āĻ“ āĻĻā§āϰ⧁āϤ āĻŦ⧁āĻā§‡ āĻĢ⧇āϞāϤ⧇ āĻĒāĻžāϰ⧇āύ āĻāϟāĻž āϕ⧀ āĻ•āϰāϛ⧇, āϕ⧋āύ SQL āϚāĻžāϞāĻžāϝāĻŧ, āĻāĻŦāĻ‚ āϕ⧋āύ āĻāϰāϰāϗ⧁āϞ⧋ āĻĢ⧇āϰāϤ āĻĻāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤\n\nāϞāĻ•ā§āĻˇā§āϝāϗ⧁āϞ⧋ āϏāĻšāϜ:\n\n- āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āϟāĻžāχāĻĒ āϏ⧇āĻĢāϟāĻŋ (IDs, entities, āĻ“ results any āύāϝāĻŧ)\n- āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻžāϗ⧁āϞ⧋ āωāĻĻā§āĻĻ⧇āĻļā§āϝ āĻŦā§āϝāĻžāĻ–ā§āϝāĻž āĻ•āϰāĻŦ⧇ āϟāĻžāχāϟ āϟāĻžāχāĻĒ-āϜāĻŋāĻŽāĻ¨ā§āϝāĻžāĻ¸ā§āϟāĻŋāĻ•āϏ āĻ›āĻžāĻĄāĻŧāĻž\n- āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āφāϚāϰāĻŖ āϞ⧁āϕ⧋āϝāĻŧ āύāĻž āĻāĻŽāύāĻ­āĻžāĻŦ⧇ āĻŦ⧁āϝāĻŧāĻžāχāϞāĻžāϰāĻĒā§āϞ⧇āϟ āĻ•āĻŽāĻžāύ⧋\n- List/Get/Create/Update/Delete āϜ⧁āĻĄāĻŧ⧇ āϧāĻžāϰāĻžāĻŦāĻžāĻšāĻŋāĻ• āφāϚāϰāĻŖ\n\nāύāύ-āĻ—ā§‹āϞāϗ⧁āϞ⧋āĻ“ āϏāĻŽāĻžāύāĻ­āĻžāĻŦ⧇ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖāĨ¤ āĻāϟāĻž ORM āύāϝāĻŧāĨ¤ āĻāϟāĻŋ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž, āĻŸā§‡āĻŦāĻŋāϞ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ join āĻ•āϰāĻž, āĻŦāĻž āύ⧀āϰāĻŦāĻ­āĻžāĻŦ⧇ āϕ⧋āϝāĻŧ⧇āϰāĻŋ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻž āωāϚāĻŋāϤ āύāϝāĻŧāĨ¤ “āĻŽā§āϝāĻžāϜāĻŋāĻ• āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚â€ āφāĻĒāύāĻžāϕ⧇ āĻĒ⧁āύāϰāĻžāϝāĻŧ reflection, āĻŸā§āϝāĻžāĻ—āϏ, āĻ“ āĻāϜ āϕ⧇āϏ⧇ āĻĢāĻŋāϰ⧇ āύāĻŋāϝāĻŧ⧇ āϝāĻžāĻŦ⧇āĨ¤\n\nāϏāĻžāϧāĻžāϰāĻŖ SQL āĻ“āϝāĻŧāĻžāĻ°ā§āĻ•āĻĢā§āϞ⧋ āϧāϰ⧇ āύāĻŋāύ: āĻ¸ā§āĻĒāĻˇā§āϟ SQL (āĻŦāĻž āĻĒāĻžāϤāϞāĻž query builder), āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ āϏ⧀āĻŽāĻž, āĻāĻŦāĻ‚ āĻāĻŽāύ āĻāϰāϰāϗ⧁āϞ⧋ āϝ⧇āϗ⧁āϞ⧋ āφāĻĒāύāĻŋ āĻŦāĻŋāĻŦ⧇āϚāύāĻž āĻ•āϰ⧇ āϏāĻŽāĻ¸ā§āϝāĻž āĻŦ⧁āĻāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύāĨ¤ āϝāĻ–āύ āĻ•āĻŋāϛ⧁ āĻŦā§āϝāĻ°ā§āĻĨ āĻšāϝāĻŧ, āĻāϰāϰāϟāĻž āĻŦāϞāĻŦ⧇ “not found”, “conflict/constraint violation”, āĻŦāĻž “DB unavailable” — āύāĻž āϝ⧇ āĻ…āĻ¸ā§āĻĒāĻˇā§āϟ “repository error”āĨ¤\n\nāϕ⧀ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāϤ⧇ āĻšāĻŦ⧇ āϤāĻž āĻšāϞ⧋ āϕ⧋āύāϟāĻž āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻšāĻŦ⧇ āĻāĻŦāĻ‚ āϕ⧋āύāϟāĻž āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ āϜāĻ¨ā§āϝ āϰ⧇āϖ⧇ āĻĻ⧇āĻŦ⧇āύāĨ¤\n\n- āĻœā§‡āύ⧇āϰāĻŋāĻ•: āĻĒā§āϰāĻŦāĻžāĻš (āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāύ⧋, āĻ¸ā§āĻ•ā§āϝāĻžāύ, āϟāĻžāχāĻĒ⧇āĻĄ āĻ­ā§āϝāĻžāϞ⧁ āĻĢ⧇āϰāϤ, āϏāĻžāϧāĻžāϰāĻŖ āĻāϰāϰ āĻ…āύ⧁āĻŦāĻžāĻĻ)āĨ¤\n- āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ: āĻ…āĻ°ā§āĻĨ (āĻŸā§‡āĻŦāĻŋāϞ āύāĻžāĻŽ, āϏāĻŋāϞ⧇āĻ•ā§āϟ āĻ•āϰāĻž āĻ•āϞāĻžāĻŽ, joins, āĻāĻŦāĻ‚ SQL āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚)āĨ¤\n\nāϏāĻŦ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϕ⧇ āĻāĻ•āĻ• āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡ āĻœā§‹āϰ āĻ•āϰāϞ⧇ āϕ⧋āĻĄ āĻĒāĻĄāĻŧāϤ⧇ āĻ•āĻ āĻŋāύ āĻšāϝāĻŧ⧇ āĻĒāĻĄāĻŧ⧇ — āĻ…āύ⧇āĻ•āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ two clear queries āϞ⧇āĻ–āĻž āϏāĻšāϜāĨ¤\n\n## āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻāĻŦāĻ‚ ID āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻž āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰāĻž\n\nāĻ…āϧāĻŋāĻ•āĻžāĻ‚āĻļ CRUD āϕ⧋āĻĄ āĻŦāĻžāϰāĻŦāĻžāϰ āĻšāϝāĻŧ āĻ•āĻžāϰāĻŖ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻŸā§‡āĻŦāĻŋāϞ⧇ āĻāĻ•āχ āĻŽā§ŒāϞāĻŋāĻ• āĻ•āĻžāϜāϗ⧁āϞ⧋ āφāϛ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ āύāĻŋāϜāĻ¸ā§āĻŦ āĻĢāĻŋāĻ˛ā§āĻĄāĨ¤ āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āύāĻŋāϝāĻŧ⧇ āĻ•ā§ŒāĻļāϞ āĻšāϞ āĻāĻ•āϟāĻŋ āϛ⧋āϟ āĻļ⧇āĻĒ āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āĻāĻŦāĻ‚ āĻŦāĻžāĻ•āĻŋāϟāĻž āĻŽā§āĻ•ā§āϤ āϰāĻžāĻ–āĻžāĨ¤\n\nāĻļ⧁āϰ⧁āϤ⧇ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāύ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋāϟāĻŋ āĻŦāĻžāĻ¸ā§āϤāĻŦ⧇ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āϕ⧀ āϜāĻžāύāϤ⧇ āĻšāĻŦ⧇āĨ¤ āĻ…āύ⧇āĻ• āĻĻāϞ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āĻŽāĻžāĻ¤ā§āϰ āϏāĻžāĻ°ā§āĻŦāϜāύ⧀āύ āĻ…āĻ‚āĻļ āĻšāϞ IDāĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āĻĻāϰāĻ•āĻžāϰ āĻšāϤ⧇ āĻĒāĻžāϰ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ āϏāĻŦ āϜāĻžāϝāĻŧāĻ—āĻžāϝāĻŧ āĻŦāĻžāĻ§ā§āϝ āĻ•āϰāϞ⧇ āĻŽāĻĄā§‡āϞ āĻ­āĻžāύāϚāĻŋ āĻŽāύ⧇ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤\n\n### āĻāĻŽāύ āĻāĻ•āϟāĻŋ ID āϟāĻžāχāĻĒ āĻŦ⧇āϛ⧇ āύāĻŋāύ āϝāĻžāϤ⧇ āφāĻĒāύāĻŋ āĻĨāĻžāĻ•āϤ⧇ āĻĒāĻžāϰ⧇āύ\n\nāφāĻĒāύāĻžāϰ ID āϟāĻžāχāĻĒāϟāĻŋ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ⧇ āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āϏāĻžāϰāĻŋ āĻļāύāĻžāĻ•ā§āϤ āĻšāϝāĻŧ āϤāĻžāϰ āĻŽāĻŋāϞ āϰāĻžāĻ–āĻž āωāϚāĻŋāϤāĨ¤ āĻ•āĻŋāϛ⧁ āĻĒā§āϰāĻœā§‡āĻ•ā§āϟ int64 āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āĻ…āĻ¨ā§āϝāϰāĻž UUID stringāĨ¤ āϝāĻĻāĻŋ āĻāĻ•āϟāĻŋ āĻ…āĻ­āĻŋāĻ¨ā§āύ āĻĒāĻĻā§āϧāϤāĻŋ āϏāĻžāĻ°ā§āĻ­āĻŋāϏāϜ⧁āĻĄāĻŧ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇, āϤāĻžāĻšāϞ⧇ ID āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ•āϰ⧁āύāĨ¤ āϝāĻĻāĻŋ āĻĒ⧁āϰ⧋ āϕ⧋āĻĄāĻŦ⧇āϏ āĻāĻ•āϟāĻŋ ID āϟāĻžāχāĻĒ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āϏ⧇āϟāĻŋ āĻĢāĻŋāĻ•ā§āϏ āĻ•āϰ⧇ āϰāĻžāĻ–āĻž āϏāĻŋāĻ—āύ⧇āϚāĻžāϰ āϛ⧋āϟ āϰāĻžāĻ–āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤\n\nāĻāĻ•āϟāĻŋ āĻ­āĻžāϞ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•āύāĻ¸ā§āĻŸā§āϰ⧇āχāĻ¨ā§āϟ āĻšāϞ⧋ comparable, āĻ•āĻžāϰāĻŖ āφāĻĒāύāĻŋ ID āϤ⧁āϞāύāĻž āĻ•āϰāĻŦ⧇āύ, map āϕ⧀ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ, āĻāĻŦāĻ‚ āĻāϗ⧁āϞ⧋ āĻĒāĻžāϏ āĻ•āϰāĻŦ⧇āύāĨ¤\n\ngo\ntype ID interface {\n\tcomparable\n}\n\ntype Entity[IDT ID] interface {\n\tGetID() IDT\n\tSetID(IDT)\n}\n\n\n### āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻ•āύāĻ¸ā§āĻŸā§āϰ⧇āχāĻ¨ā§āϟ āϝāϤāϟāĻž āϏāĻŽā§āĻ­āĻŦ āϛ⧋āϟ āϰāĻžāϖ⧁āύ\n\nstruct embedding āĻŦāĻž ~struct{...} āĻŽāϤ⧋ āϟāĻžāχāĻĒ-āϏ⧇āϟ āĻŸā§āϰāĻŋāĻ•āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦāĻžāĻ§ā§āϝ āĻ•āϰāĻž āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϚāϞ⧁āύāĨ¤ āĻāϗ⧁āϞ⧋ āĻļāĻ•ā§āϤāĻŋāĻļāĻžāϞ⧀ āĻĻ⧇āĻ–āĻžāϝāĻŧ, āϤāĻŦ⧇ āφāĻĒāύāĻžāϰ āĻĄā§‹āĻŽā§‡āχāύ āϟāĻžāχāĻĒāϗ⧁āϞ⧋āϕ⧇ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ⧇āϰ āϏāĻ™ā§āϗ⧇ āĻœā§‹āĻĄāĻŧ⧇ āĻĻ⧇āĻŦ⧇āĨ¤\n\nāĻŦāϰāĻ‚, āϕ⧇āĻŦāϞ āϏ⧇āχāϗ⧁āϞ⧋ āĻĻāĻžāĻŦāĻŋ āĻ•āϰ⧁āύ āϝ⧇āϗ⧁āϞ⧋ āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ CRUD āĻĒā§āϰāĻŦāĻžāĻšāϕ⧇ āϞāĻžāϗ⧇:\n\n- ID āĻĒ⧇āϤ⧇ āĻ“ āϏ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻž (āϤāĻžāϤ⧇ Create ID āĻĢ⧇āϰāϤ āĻĻāĻŋāϤ⧇ āĻĒāĻžāϰ⧇, āĻāĻŦāĻ‚ Update/Delete āϞāĻ•ā§āĻˇā§āϝ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇)\n\nāĻĒāϰ⧇ āϝāĻĻāĻŋ soft deletes āĻŦāĻž optimistic locking āϝ⧋āĻ— āĻ•āϰāϤ⧇ āϚāĻžāύ, āϛ⧋āϟ opt-in āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āϝ⧋āĻ— āĻ•āϰ⧁āύ (āωāĻĻāĻžāĻšāϰāĻŖ: GetVersion/SetVersion) āĻāĻŦāĻ‚ āϕ⧇āĻŦāϞ āϝ⧇āĻ–āĻžāύ⧇ āĻĻāϰāĻ•āĻžāϰ āϏ⧇āĻ–āĻžāύ⧇āχ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤ āϛ⧋āϟ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏāϗ⧁āϞ⧋ āϏāĻžāϧāĻžāϰāύāϤ āĻ­āĻžāϞ⧋āĻ­āĻžāĻŦ⧇ āĻŦāϝāĻŧāϏ āĻŦāĻžāĻĄāĻŧāĻžāϝāĻŧāĨ¤\n\n## āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āϝāĻž āĻĒāĻĄāĻŧāϤ⧇ āϏāĻšāϜ āĻĨāĻžāĻ•āĻŦ⧇\n\nāϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ⧇ āφāĻĒāύāĻŋ āϝāĻž āϚāĻžāύ āϤāĻž āĻŦāĻ°ā§āĻŖāύāĻž āĻ•āϰ⧁āύ, āύāĻž āϝ⧇ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇ āϤāĻžāĨ¤ āϝāĻĻāĻŋ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ SQL-āϰ āĻŽāϤ⧋ āĻšāϝāĻŧ, āϤāĻž āϏāĻ°ā§āĻŦāĻ¤ā§āϰ āĻĄāĻŋāĻŸā§‡āχāϞ āĻĢāĻžāρāϏ āĻ•āϰ⧇āĨ¤\n\nāĻŽā§‡āĻĨāĻĄ āϏ⧇āϟ āϛ⧋āϟ āĻ“ āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āϰāĻžāϖ⧁āύāĨ¤ context.Context āĻĒā§āϰāĻĨāĻŽ āĻĻāĻŋāύ, āϤāĻžāϰāĻĒāϰ āĻĒā§āϰāϧāĻžāύ āχāύāĻĒ⧁āϟ (ID āĻŦāĻž āĻĄā§‡āϟāĻž), āϤāĻžāϰāĻĒāϰ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āύāĻ•āϏāϗ⧁āϞ⧋ āĻāĻ•āϟāĻŋ struct-āĻ āĻŦāĻžāĻ¨ā§āĻĄā§‡āϞ āĻ•āϰ⧁āύāĨ¤\n\ngo\ntype Repository[T any, ID comparable, CreateIn any, UpdateIn any, ListQ any] interface {\n\tGet(ctx context.Context, id ID) (T, error)\n\tList(ctx context.Context, q ListQ) ([]T, error)\n\tCreate(ctx context.Context, in CreateIn) (T, error)\n\tUpdate(ctx context.Context, id ID, in UpdateIn) (T, error)\n\tDelete(ctx context.Context, id ID) error\n}\n\n\nList-āĻāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āϏāĻžāĻ°ā§āĻŦāϜāύ⧀āύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āϟāĻžāχāĻĒ āĻœā§‹āϰ āύāĻž āĻ•āϰāĻž āĻ­āĻžāϞāĨ¤ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰāϗ⧁āϞ⧋āχ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϗ⧁āϞ⧋āϰ āĻŽāĻ§ā§āϝ⧇ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āφāϞāĻžāĻĻāĻžāĨ¤ āĻāĻ•āϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻŋāĻ• āĻĒāĻĻā§āϧāϤāĻŋ āĻšāϞ⧋ āĻĒā§āϰāϤāĻŋ-āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āϕ⧁āϝāĻŧ⧇āϰāĻŋ āϟāĻžāχāĻĒ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āϛ⧋āϟ āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ pagination shape āφāĻĒāύāĻŋ embed āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤\n\ngo\ntype Page struct {\n\tLimit int\n\tOffset int\n}\n\n\nāĻāϰāϰ āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻŋāĻ‚ āĻāĻŽāύ āϜāĻžāϝāĻŧāĻ—āĻž āϝ⧇āĻ–āĻžāύ⧇ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϰāĻžāϝāĻŧāĻļāχ āĻ—ā§‹āϞāĻŽāĻžāϞ āĻ•āϰ⧇āĨ¤ āφāϗ⧇āχ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāύ āϕ⧋āύ āĻāϰāϰāϗ⧁āϞ⧋āϰ āωāĻĒāϰ āĻ•āϞāĻžāϰāϰāĻž āĻļāĻžāĻ–āĻž āĻ•āϰāĻŦ⧇āĨ¤ āϏāĻžāϧāĻžāϰāĻŖ āϏ⧇āϟāϟāĻŋ āĻ•āĻžāϜ āĻ•āϰ⧇:\n\n- ErrNotFound āϝāĻ–āύ āϕ⧋āύ⧋ ID āύ⧇āχ\n- ErrConflict āχāωāύāĻŋāĻ• āĻŦā§āϝāĻ°ā§āĻĨāϤāĻž āĻŦāĻž āĻ­āĻžāĻ°ā§āϏāύ āϏāĻ‚āϘāĻ°ā§āώ⧇āϰ āϜāĻ¨ā§āϝ\n- ErrValidation āχāύāĻĒ⧁āϟ āĻ…āĻŦ⧈āϧ āĻšāϞ⧇ (āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āϝāĻĻāĻŋ āϰāĻŋāĻĒā§‹ āĻ­ā§āϝāĻžāϞāĻŋāĻĄā§‡āϟ āĻ•āϰ⧇)\n\nāĻŦāĻžāĻ•āĻŋ āϏāĻŦāĻ•āĻŋāϛ⧁ low-level āĻāϰāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ wrap āĻ•āϰ⧁āύ (DB/network)āĨ¤ āĻāχ āĻ•āύāĻŸā§āϰāĻžāĻ•ā§āϟ āĻĨāĻžāĻ•āϞ⧇ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āϕ⧋āĻĄ “not found” āĻŦāĻž “conflict” āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇ āϤāĻžāϰ āĻĒāϰ⧇āϰ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ āĻ•āĻŋ PostgreSQL āύāĻžāĻ•āĻŋ āĻ…āĻ¨ā§āϝ āĻ•āĻŋāϛ⧁ āϏ⧇āϟāĻž āύāĻŋāϝāĻŧ⧇ āϚāĻŋāĻ¨ā§āϤāĻž āύāĻž āĻ•āϰ⧇āĨ¤\n\n## āĻ•āĻŋāĻ­āĻžāĻŦ⧇ reflection āĻ›āĻžāĻĄāĻŧāĻžāχ āĻĢā§āϞ⧋ reuse āĻ•āϰāĻŦ⧇āύ\n\nReflection āϏāĻžāϧāĻžāϰāĻŖāϤ āϤāĻ–āύ āĻĒā§āϰāĻŦ⧇āĻļ āĻ•āϰ⧇ āϝāĻ–āύ āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āϕ⧋āĻĄ āϚāĻžāύ āϝāĻž “āϕ⧋āύāĻ“ struct-āχ āĻĒā§‚āϰāĻŖ āĻ•āϰāĻŦā§‡â€āĨ¤ āĻāϟāĻŋ āĻ¤ā§āϰ⧁āϟāĻŋāϗ⧁āϞ⧋ runtime-āĻ āϞ⧁āĻ•āĻžāϝāĻŧ āĻāĻŦāĻ‚ āύāĻŋāϝāĻŧāĻŽāϗ⧁āϞ⧋ āĻ…āĻ¸ā§āĻĒāĻˇā§āϟ āĻ•āϰ⧇āĨ¤\n\nāĻāĻ•āϟāĻŋ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻĒāĻĻā§āϧāϤāĻŋ āĻšāϞ⧋ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āύāĻŋāĻ¸ā§āϤ⧇āϜ āĻ…āĻ‚āĻļāϗ⧁āϞ⧋ reuse āĻ•āϰāĻž: āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāύ⧋, āϰ⧋ āϞ⧁āĻĒ, āĻĒā§āϰāĻ­āĻžāĻŦāĻŋāϤ āϰ⧋ āĻ—āĻŖāύāĻž āĻšā§‡āĻ•, āĻāĻŦāĻ‚ āĻāϰāϰāϗ⧁āϞāĻŋ āϧāĻžāϰāĻžāĻŦāĻžāĻšāĻŋāĻ•āĻ­āĻžāĻŦ⧇ wrap āĻ•āϰāĻžāĨ¤ struct-āĻ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻ¸ā§āĻĒāĻˇā§āϟ āϰāĻžāϖ⧁āύāĨ¤\n\n### āĻĻāĻžāϝāĻŧāĻŋāĻ¤ā§āĻŦ āĻ­āĻžāĻ— āĻ•āϰ⧁āύ: SQL, āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚, āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĢā§āϞ⧋\n\nāĻĒā§āϰāĻžāϝāĻŧā§‹āĻ—āĻŋāĻ• āĻ­āĻžāĻ—āĻĻāĻžāϰāĻŋ āĻĻ⧇āĻ–āϤ⧇ āĻāĻŽāύ āĻšāϤ⧇ āĻĒāĻžāϰ⧇:\n\n- āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ: SQL āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚ āĻ“ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰ āĻ…āĻ°ā§āĻĄāĻžāϰ āĻāĻ• āĻ¸ā§āĻĨāĻžāύ⧇ āϰāĻžāϖ⧁āύ\n- āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ: āϛ⧋āϟ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻĢāĻžāĻ‚āĻļāύ āϞāĻŋāϖ⧁āύ āϝāĻž āϰ⧋āϕ⧇ āĻ•āύāĻ•ā§āϰāĻŋāϟ struct-āĻ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰ⧇\n- āĻœā§‡āύ⧇āϰāĻŋāĻ•: āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĢā§āϞ⧋ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰāĻŦ⧇ āϝāĻž āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāϝāĻŧ āĻāĻŦāĻ‚ āĻŽā§āϝāĻžāĻĒāĻžāϰ āĻ•āϞ āĻ•āϰ⧇\n\nāĻāĻ­āĻžāĻŦ⧇ āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ•āĻŽāĻžāϝāĻŧ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻ•āĻŋ āĻ•āϰāϛ⧇ āϏ⧇āϟāĻž āϞ⧁āĻ•āĻžāϝāĻŧ āύāĻžāĨ¤\n\nāύāĻŋāĻšā§‡ āĻāĻ•āϟāĻŋ āϛ⧋āϟ āĻ…ā§āϝāĻžāĻŦāĻ¸ā§āĻŸā§āϰāĻžāĻ•āĻļāύ āφāϛ⧇ āϝāĻž *sql.DB āĻŦāĻž *sql.Tx āĻĻ⧁āĻŸā§‹āϕ⧇āχ āĻĒāĻžāϏ āĻ•āϰāϤ⧇ āĻĻ⧇āϝāĻŧ: \n\ngo\ntype DBTX interface {\n\tExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)\n\tQueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)\n\tQueryRowContext(ctx context.Context, query string, args ...any) *sql.Row\n}\n\n\n### āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āϕ⧀ āĻ•āϰāĻž āωāϚāĻŋāϤ (āĻ“ āϕ⧀ āύāĻž)\n\nāĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ¸ā§āϤāϰāϟāĻŋ āφāĻĒāύāĻžāϰ struct “āĻŦā§‹āĻāĻžāĻ°â€ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰāĻŦ⧇ āύāĻžāĨ¤ āĻŦāϰāĻ‚ āĻāϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āĻĢāĻžāĻ‚āĻļāύ āύ⧇āĻŦ⧇, āϝ⧇āĻŽāύ:\n\n- āĻāĻ•āϟāĻŋ binder āϝāĻž āχāύāĻĒ⧁āϟāϕ⧇ āϕ⧋āϝāĻŧ⧇āϰāĻŋ āφāĻ°ā§āĻ—āϏ-āĻ āĻĒāϰāĻŋāĻŖāϤ āĻ•āϰ⧇\n- āĻāĻ•āϟāĻŋ scanner āϝāĻž āĻ•āϞāĻžāĻŽāϗ⧁āϞ⧋ entity-āϤ⧇ āĻĒāĻĄāĻŧ⧇\n\nāωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ, āĻāĻ•āϟāĻŋ Customer āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ SQL āĻ•āύāĻ¸ā§āĻŸā§āϝāĻžāĻ¨ā§āϟ (selectByID, insert, update) āĻšāĻŋāϏāĻžāĻŦ⧇ āϰāĻžāϖ⧇ āĻāĻŦāĻ‚ āĻāĻ•āĻŦāĻžāϰ scanCustomer(rows) āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āϟ āĻ•āϰ⧇āĨ¤ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• List āϞ⧁āĻĒ, context, āĻ“ āĻāϰāϰ wrapping āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāĻŦ⧇, āφāϰ scanCustomer āϟāĻžāχāĻĒ-āϏ⧇āĻĢāϟāĻŋ āĻ“ āĻ¸ā§āĻĒāĻˇā§āϟ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āϰāĻžāĻ–āĻŦ⧇āĨ¤\n\nāύāϤ⧁āύ āĻ•āϞāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰāϞ⧇ SQL āĻ“ scanner āφāĻĒāĻĄā§‡āϟ āĻ•āϰ⧁āύ — āĻ•āĻŽā§āĻĒāĻžāχāϞāĻžāϰāχ āφāĻĒāύāĻžāϕ⧇ āĻŦāϞāĻŦ⧇ āϕ⧀ āĻ­āĻžāϙ⧇āϛ⧇āĨ¤\n\n## āϧāĻžāĻĒ⧇ āϧāĻžāĻĒ⧇: āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āϟ āĻ•āϰāĻž\n\nāϞāĻ•ā§āĻˇā§āϝ āĻšāϞ⧋ List/Get/Create/Update/Delete-āĻāϰ āϜāĻ¨ā§āϝ āĻāĻ• reusable āĻĢā§āϞ⧋ āϝ⧇āϟāĻž āĻĒā§āϰāϤāĻŋāϟāĻŋ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋāϕ⧇ āϤāĻžāϰ SQL āĻ“ āϰ⧋ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āĻ¸ā§Ž āϰāĻžāϖ⧇āĨ¤\n\n### 1) āϕ⧋āϰ āϟāĻžāχāĻĒāϗ⧁āϞ⧋ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰ⧁āύ\n\nāĻ•āĻŽāĻĒāĻ•ā§āώ⧇ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻž āĻĻāĻŋāϝāĻŧ⧇ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύāĨ¤ āφāĻĒāύāĻžāϰ āϕ⧋āĻĄāĻŦ⧇āϏ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ ID āϟāĻžāχāĻĒ āĻŦ⧇āϛ⧇ āύāĻŋāύ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āϰāĻžāϖ⧁āύāĨ¤\n\ngo\ntype ID interface{ ~int64 | ~string }\n\ntype Repo[E any, K ID] interface {\n\tGet(ctx context.Context, id K) (E, error)\n\tList(ctx context.Context, limit, offset int) ([]E, error)\n\tCreate(ctx context.Context, e *E) error\n\tUpdate(ctx context.Context, e *E) error\n\tDelete(ctx context.Context, id K) error\n}\n\n\n### 2) DB āĻ“ āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ executor āϝ⧋āĻ— āĻ•āϰ⧁āύ\n\nāĻœā§‡āύ⧇āϰāĻŋāĻ• āϕ⧋āĻĄ āϏāϰāĻžāϏāϰāĻŋ *sql.DB āĻŦāĻž *sql.Tx-āĻāϰ āϏāĻ™ā§āϗ⧇ āĻŦāĻžāρāϧāĻŦ⧇āύ āύāĻžāĨ¤ āĻāĻ•āϟāĻŋ āϛ⧋āϟ executor āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ⧇āϰ āωāĻĒāϰ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰ⧁āύ āϝāĻž āφāĻĒāύāĻŋ āĻĄāĻžāϕ⧇ (QueryContext, ExecContext, QueryRowContext)āĨ¤ āϏ⧇āĻŦāĻž āĻĨ⧇āϕ⧇ DB āĻŦāĻž āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ āĻĒāĻžāϏ āĻ•āϰāϞ⧇ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋāϰ āϕ⧋āĻĄ āĻŦāĻĻāϞāĻžāϤ⧇ āĻšāĻŦ⧇ āύāĻžāĨ¤\n\n### 3) āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĢā§āϞ⧋ āϏāĻš āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻŦ⧇āϏ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ\n\nbaseRepo[E,K] āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ āϝāĻž executor āĻāĻŦāĻ‚ āĻ•āĻŋāϛ⧁ āĻĢāĻžāĻ‚āĻļāύ āĻĢāĻŋāĻ˛ā§āĻĄ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āĨ¤ āĻŦ⧇āϏāϟāĻŋ āĻŦāĻŋāϰāĻ•ā§āϤāĻŋāĻ•āϰ āĻ•āĻžāϜāϗ⧁āϞ⧋ āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāĻŦ⧇: āϕ⧋āϝāĻŧ⧇āϰāĻŋ āĻ•āϞ āĻ•āϰāĻž, “not found” āĻŽā§āϝāĻžāĻĒ āĻ•āϰāĻž, āĻĒā§āϰāĻ­āĻžāĻŦāĻŋāϤ āϰ⧋ āĻšā§‡āĻ• āĻ•āϰāĻž, āĻāĻŦāĻ‚ āĻ•āύāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϟ āĻāϰāϰ āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧāĻžāĨ¤\n\n### 4) āĻāĻ¨ā§āϟāĻŋāϟāĻŋ-āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ…āĻ‚āĻļāϗ⧁āϞ⧋ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āϟ āĻ•āϰ⧁āύ\n\nāĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āϰāĻŋāĻĒā§‹ āϝāĻž āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ•āϰāĻž āϝāĻžāϝāĻŧ āύāĻž āϏ⧇āϟāĻžāχ āϏāϰāĻŦāϰāĻžāĻš āĻ•āϰāĻŦ⧇:\n\n- list/get/create/update/delete-āĻāϰ SQL\n- scan(row) āĻĢāĻžāĻ‚āĻļāύ āϝāĻž āϰ⧋āϕ⧇ E-āϤ⧇ āϰ⧂āĻĒāĻžāĻ¨ā§āϤāϰ āĻ•āϰ⧇\n- bind(...) āĻĢāĻžāĻ‚āĻļāύ āϝāĻž āϕ⧋āϝāĻŧ⧇āϰāĻŋ āφāĻ°ā§āĻ—āϏ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇\n\n### 5) āĻ•āύāĻ•ā§āϰāĻŋāϟ āϰāĻŋāĻĒā§‹āĻ“āϝāĻŧāĻžāϗ⧁āϞ⧋ āĻ“ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ⧇ āĻ“āϗ⧁āϞ⧋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ\n\nNewCustomerRepo(exec Executor) *CustomerRepo āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ āϝāĻž baseRepo embed āĻŦāĻž wrap āĻ•āϰ⧇āĨ¤ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āϞ⧇āϝāĻŧāĻžāϰ Repo[E,K] āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ⧇ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύ⧇āϝāĻŧ āĻ•āĻ–āύ āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ āĻļ⧁āϰ⧁ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇; āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϕ⧇āĻŦāϞ āϝ⧇ executor āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϝāĻŧ⧇āϛ⧇ āϏ⧇āϟāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āĨ¤\n\n## List/Get/Create/Update/Delete āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāĻž āϝāĻžāϤ⧇ āĻāĻžāĻŽā§‡āϞāĻž āύāĻž āφāϏ⧇\n\nāĻœā§‡āύ⧇āϰāĻŋāĻ• āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϤāĻ–āύāχ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇ āϝāĻ–āύ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻŽā§‡āĻĨāĻĄ āϏāĻŦ āϜāĻžāϝāĻŧāĻ—āĻžāϝāĻŧ āĻāĻ•āχāĻ­āĻžāĻŦ⧇ āφāϚāϰāĻŖ āĻ•āϰ⧇āĨ¤ āĻŦ⧇āĻļāĻŋāϰāĻ­āĻžāĻ— āϏāĻŽāĻ¸ā§āϝāĻž āϛ⧋āϟ āĻ…āύāĻŋāϝāĻŧāĻŽ āĻĨ⧇āϕ⧇ āφāϏ⧇: āĻāĻ• āϰāĻŋāĻĒā§‹ created_at āĻĻāĻŋāϝāĻŧ⧇ order āĻ•āϰ⧇, āĻ…āĻ¨ā§āϝāϟāĻŋ id āĻĻāĻŋāϝāĻŧ⧇; āĻāĻ•āϟāĻŋ nil, nil āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧ missing rows-āĻ, āĻ…āĻ¨ā§āϝāϟāĻŋ āĻāϰāϰ āĻĻ⧇āϝāĻŧāĨ¤\n\n### List: pagination āĻ“ ordering āϝāĻžāϤ⧇ āφāχāĻŸā§‡āĻŽ āϏāϰ⧇ āύāĻž āϝāĻžāϝāĻŧ\n\nāĻāĻ•āϟāĻŋ pagination āĻ¸ā§āϟāĻžāχāϞ āĻŦ⧇āϛ⧇ āύāĻŋāύ āĻāĻŦāĻ‚ āϤāĻž āϧāĻžāϰāĻžāĻŦāĻžāĻšāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰ⧁āύāĨ¤ Offset pagination (limit/offset) āϏāĻŋāĻŽā§āĻĒāϞ āĻāĻŦāĻ‚ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āϜāĻ¨ā§āϝ āĻ­āĻžāϞāĨ¤ Cursor pagination āĻ…āύāĻ¨ā§āϤ āĻ¸ā§āĻ•ā§āϰāϞāĻŋāĻ‚āϝāĻŧ⧇āϰ āϜāĻ¨ā§āϝ āĻ­āĻžāϞ⧋, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ sort key āϚāĻžāχāĨ¤\n\nāϝāĻž āĻ•āĻŋāϛ⧁ āĻŦ⧇āϛ⧇ āύāĻŋāύ, ordering āĻ¸ā§āĻĒāĻˇā§āϟ āĻ“ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āϰāĻžāϖ⧁āύāĨ¤ āχāωāύāĻŋāĻ• āĻ•āϞāĻžāĻŽ (āĻĒā§āϰāĻžāχāĻŽāĻžāϰāĻŋ āϕ⧀) āĻĻāĻŋāϝāĻŧ⧇ order āĻ•āϰāϞ⧇ āύāϤ⧁āύ āϰ⧇āĻ•āĻ°ā§āĻĄ āĻāϞ⧇ āφāχāĻŸā§‡āĻŽ āĻĒ⧇āϜ āĻŦāĻĻāϞ⧇ āϝāĻžāĻŦ⧇ āύāĻžāĨ¤\n\n### Get: āĻ¸ā§āĻĒāĻˇā§āϟ “not found” āϏāĻŋāĻ—āĻ¨ā§āϝāĻžāϞ\n\nGet(ctx, id) āĻāĻ•āϟāĻŋ āϟāĻžāχāĻĒ⧇āĻĄ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻ“ āĻ¸ā§āĻĒāĻˇā§āϟ āĻŽāĻŋāϏāĻŋāĻ‚-āϰ⧇āĻ•āĻ°ā§āĻĄ āϏāĻ‚āϕ⧇āϤ āĻĢ⧇āϰāϤ āĻĻāĻŋāύ — āϏāĻžāϧāĻžāϰāĻŖāϤ ErrNotFound āĻŽāϤ⧋ āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ sentinelāĨ¤ āĻļā§‚āĻ¨ā§āϝ-āĻŽā§‚āĻ˛ā§āϝ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻ“ nil āĻāϰāϰ āĻĢ⧇āϰāϤ āĻĻāĻŋāϞ⧇ āĻ•āϞāĻžāϰāϰāĻž āĻŦ⧁āĻāϤ⧇ āĻĒāĻžāϰāĻŦ⧇ āύāĻž “āĻŽāĻŋāϏāĻŋāĻ‚â€ āύāĻž “āĻ–āĻžāϞāĻŋ āĻĢāĻŋāĻ˛ā§āĻĄâ€āĨ¤\n\nāϟāĻžāχāĻĒ āĻĄā§‡āϟāĻžāϰ āϜāĻ¨ā§āϝ, āĻāϰāϰ āĻ¸ā§āĻŸā§‡āĻŸā§‡āϰ āϜāĻ¨ā§āϝāĨ¤\n\nāĻŽā§‡āĻĨāĻĄ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āϟ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻ•āϝāĻŧ⧇āĻ•āϟāĻŋ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāύ āĻāĻŦāĻ‚ āϏ⧇āϗ⧁āϞ⧋ āĻ•āύāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϟ āϰāĻžāϖ⧁āύ:\n\n- Create: āφāĻĒāύāĻŋ āĻ•āĻŋ āχāύāĻĒ⧁āϟ āϟāĻžāχāĻĒ āύ⧇āĻŦ⧇āύ (āϕ⧋āύ ID, āϕ⧋āύ timestamps āύāϝāĻŧ) āύāĻžāĻ•āĻŋ āĻĒā§‚āĻ°ā§āĻŖ entity? āĻ…āύ⧇āĻ• āĻĻāϞ Create(ctx, in CreateX) āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰ⧇ āϝāĻžāϤ⧇ āĻ•āϞāĻžāϰāϰāĻž āϏāĻžāĻ°ā§āĻ­āĻžāϰ-āύāĻŋāϝāĻŧāĻ¨ā§āĻ¤ā§āϰāĻŋāϤ āĻĢāĻŋāĻ˛ā§āĻĄ āϏ⧇āϟ āĻ•āϰāϤ⧇ āύāĻž āĻĒāĻžāϰ⧇āĨ¤\n- Update: āĻāϟāĻŋ āĻĒā§‚āĻ°ā§āĻŖ āϰāĻŋāĻĒā§āϞ⧇āϏ āύāĻžāĻ•āĻŋ āĻĒā§āϝāĻžāϚ? āĻĒā§āϝāĻžāϚ āĻšāϞ⧇ plain struct āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ āύāĻž āϝ⧇āĻ–āĻžāύ⧇ āĻļā§‚āĻ¨ā§āϝ āĻŽāĻžāύ āĻŦāĻŋāĻ­ā§āϰāĻžāĻ¨ā§āϤāĻŋāĻ•āϰāĨ¤ āĻĒāϝāĻŧ⧇āĻ¨ā§āϟāĻžāϰ, nullable āϟāĻžāχāĻĒ, āĻŦāĻž explicit field mask āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤\n- Delete: āĻšāĻžāĻ°ā§āĻĄ āĻĄāĻŋāϞ⧇āϟ āύāĻžāĻ•āĻŋ āϏāĻĢāϟ āĻĄāĻŋāϞ⧇āϟ? āϏāĻĢāϟ āĻĄāĻŋāϞ⧇āϟ āĻšāϞ⧇ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāύ Get āĻĄāĻŋāĻĢāĻ˛ā§āϟāĻ­āĻžāĻŦ⧇ āĻĄāĻŋāϞ⧇āĻŸā§‡āĻĄ āϏāĻžāϰāĻŋ āϞ⧁āĻ•āĻžāĻŦ⧇ āĻ•āĻŋ āύāĻžāĨ¤\n\nāϞāĻŋāĻ–āĻŋāϤ āĻŽā§‡āĻĨāĻĄāϗ⧁āϞ⧋ āϕ⧀ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāĻŦ⧇ āϤāĻžāĻ“ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāύāĨ¤ āĻ•āĻŽ-āφāĻļā§āϚāĻ°ā§āϝ⧇āϰ āĻ…āĻĒāĻļāύ āĻšāϞ⧋ āφāĻĒāĻĄā§‡āϟ āĻšāĻ“āϝāĻŧāĻž entity āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāĻž (DB āĻĄāĻŋāĻĢāĻ˛ā§āϟāϗ⧁āϞ⧋ āϏāĻš) āĻ…āĻĨāĻŦāĻž āĻļ⧁āϧ⧁āχ ID āĻ“ ErrNotFound āĻĢ⧇āϰāϤ āĻ•āϰāĻž āϝāĻ–āύ āĻ•āĻŋāϛ⧁ āĻŦāĻĻāϞāĻžāϝāĻŧāύāĻŋāĨ¤\n\n## āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ“ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ-āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ…āĻ‚āĻļ⧇āϰ āĻŸā§‡āĻ¸ā§āϟāĻŋāĻ‚ āĻ•ā§ŒāĻļāϞ\n\nāĻāχ āĻĒāĻĻā§āϧāϤāĻŋ āϤāĻ–āύāχ āĻŽā§‚āĻ˛ā§āϝ āĻĻ⧇āϝāĻŧ āϝāĻ–āύ āĻŦāĻŋāĻļā§āĻŦāĻžāϏ āĻ•āϰāĻž āϏāĻšāϜāĨ¤ āϕ⧋āĻĄ āϝ⧇āĻŽāύ āĻ­āĻžāĻ— āĻ•āϰ⧇āϛ⧇āύ, āĻŸā§‡āĻ¸ā§āϟāĻ“ āϏ⧇āχ āϞāĻžāχāύ⧇āχ āĻ­āĻžāĻ— āĻ•āϰ⧁āύ: āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻšā§‡āĻ˛ā§āĻĒāĻžāϰāϗ⧁āϞ⧋ āĻāĻ•āĻŦāĻžāϰ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧁āύ, āϤāĻžāϰāĻĒāϰ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ SQL āĻ“ scanning āφāϞāĻžāĻĻāĻž āĻ•āϰ⧇ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧁āύāĨ¤\n\nāĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻ…āĻ‚āĻļāϗ⧁āϞ⧋āϕ⧇ āϝāϤāϟāĻž āϏāĻŽā§āĻ­āĻŦ āϛ⧋āϟ pure āĻĢāĻžāĻ‚āĻļāύ⧇ āϰāĻžāϖ⧁āύ — pagination validation, sort key-āϕ⧇ āĻ…āύ⧁āĻŽā§‹āĻĻāĻŋāϤ āĻ•āϞāĻžāĻŽā§‡ āĻŽā§āϝāĻžāĻĒ āĻ•āϰāĻž, WHERE āĻĢā§āϰāĻžāĻ—āĻŽā§‡āĻ¨ā§āϟ āĻŦāĻŋāĻ˛ā§āĻĄāĻŋāĻ‚ āχāĻ¤ā§āϝāĻžāĻĻāĻŋāĨ¤ āĻāϗ⧁āϞ⧋ āĻĻā§āϰ⧁āϤ āχāωāύāĻŋāϟ āĻŸā§‡āĻ¸ā§āϟ āĻĻāĻŋāϝāĻŧ⧇ āĻ•āĻ­āĻžāϰ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤\n\nList āϕ⧁āϝāĻŧ⧇āϰāĻŋāĻĻ⧇āϰ āϜāĻ¨ā§āϝ table-driven āĻŸā§‡āĻ¸ā§āϟ āĻ­āĻžāϞ⧋ āĻ•āĻžāϜ āĻ•āϰ⧇ āĻ•āĻžāϰāĻŖ edge case-āχ āϏāĻŽāĻ¸ā§āϝāĻžāĨ¤ āĻ–āĻžāϞāĻŋ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ, āĻ…āϜāĻžāύāĻž sort key, limit 0, āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āĻŦ⧇āĻļāĻŋ limit, negative offset, āĻāĻŦāĻ‚ “next page” āϏ⧀āĻŽāĻžāĻ¨ā§āϤ āϝ⧇āĻ–āĻžāύ⧇ āφāĻĒāύāĻŋ āĻāĻ•ā§āϏāĻŸā§āϰāĻž āϰ⧋ āĻĢ⧇āϚ āĻ•āϰ⧇āύ — āĻāϏāĻŦ āĻ•āĻ­āĻžāϰ āĻ•āϰ⧁āύāĨ¤\n\nāĻĒā§āϰāϤāĻŋ-āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ āĻŸā§‡āĻ¸ā§āϟāϗ⧁āϞ⧋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ-āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āϕ⧀ āϤāĻž āĻĢā§‹āĻ•āĻžāϏ āϰāĻžāϖ⧁āĻ•: āφāĻĒāύāĻŋ āϕ⧋āύ SQL āφāĻļāĻž āĻ•āϰ⧇āύ āĻāĻŦāĻ‚ āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āϰ⧋ entity-āϤ⧇ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻšāϝāĻŧāĨ¤ SQL mock āĻŦāĻž āĻšāĻžāϞāĻ•āĻž āĻŸā§‡āĻ¸ā§āϟ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧁āύ scanner nulls, optional āĻ•āϞāĻžāĻŽ, āĻ“ āϟāĻžāχāĻĒ āĻ•āύāĻ­āĻžāϰāĻļāύ āϏāĻžāĻŽāϞāĻžāϝāĻŧāĨ¤\n\nāφāĻĒāύāĻžāϰ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āϝāĻĻāĻŋ āĻŸā§āϰāĻžāύāĻœā§‡āĻ•āĻļāύ āϏāĻžāĻĒā§‹āĻ°ā§āϟ āĻ•āϰ⧇, commit/rollback āφāϚāϰāĻŖ āϛ⧋āϟ fake executor āĻĻāĻŋāϝāĻŧ⧇ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧁āύ āϝ⧇ āĻ•āϞāϗ⧁āϞ⧋ āϰ⧇āĻ•āĻ°ā§āĻĄ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻāϰāϰ āϏāĻŋāĻŽā§āϞ⧇āϟ āĻ•āϰ⧇: \n\n- Begin āĻāĻ•āϟāĻŋ tx-scoped executor āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇\n- āĻ¤ā§āϰ⧁āϟāĻŋāϤ⧇ rollback āĻ āĻŋāĻ• āĻāĻ•āĻŦāĻžāϰ āĻ•āϞ āĻšāϝāĻŧ\n- āϏāĻĢāϞ āĻšāϞ⧇ commit āĻ āĻŋāĻ• āĻāĻ•āĻŦāĻžāϰ āĻ•āϞ āĻšāϝāĻŧ\n- commit āĻŦā§āϝāĻ°ā§āĻĨ āĻšāϞ⧇ āĻāϰāϰ āĻ…āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧ\n\nāĻāĻ›āĻžāĻĄāĻŧāĻžāĻ“ āϛ⧋āϟ “contract tests” āϝ⧋āĻ— āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ āϝāĻž āĻĒā§āϰāϤāĻŋāϟāĻŋ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋāϕ⧇ āĻĒāĻžāĻļ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇: create āϤāĻžāϰāĻĒāϰ get āĻāĻ•āχ āĻĄā§‡āϟāĻž, update āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦāĻĻāϞāĻžāϝāĻŧ, delete āĻ•āϰāϞ⧇ get ErrNotFound āĻĻ⧇āϝāĻŧ, āĻāĻŦāĻ‚ list āĻāĻ•āχ āχāύāĻĒ⧁āĻŸā§‡ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ ordering āĻĻ⧇āϝāĻŧāĨ¤\n\n## āϏāĻžāϧāĻžāϰāĻŖ āϭ⧁āϞ āĻ“ āĻĢāĻžāρāĻĻ\n\nāĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āĻĻ⧇āϖ⧇ āĻ…āύ⧇āϕ⧇āχ āĻāĻ•āϟāĻž āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻŦāĻžāύāĻŋāϝāĻŧ⧇ āϏāĻŦāĻ•āĻŋāϛ⧁āϕ⧇ āĻ•āĻ¨ā§āĻŸā§āϰ⧋āϞ āĻ•āϰāϤ⧇ āϚāĻžāχāĻŦ⧇āĨ¤ āĻĄāĻžāϟāĻž āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āϛ⧋āϟ āϛ⧋āϟ āĻ­āĻŋāĻ¨ā§āύāϤāĻžāϝāĻŧ āĻĒāϰāĻŋāĻĒā§‚āĻ°ā§āĻŖ, āĻāĻŦāĻ‚ āϏ⧇āχ āĻ­āĻŋāĻ¨ā§āύāϤāĻžāϗ⧁āϞ⧋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖāĨ¤\n\nāĻ•āĻŋāϛ⧁ āĻĢāĻžāρāĻĻ āĻĒā§āϰāĻžāϝāĻŧāχ āĻĻ⧇āĻ–āĻž āϝāĻžāϝāĻŧ:\n\n- āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āϏāĻžāϧāĻžāϰāĻŖāĻ•āϰāĻŖ āϝ⧇āĻ–āĻžāύ⧇ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻŽā§‡āĻĨāĻĄ āĻŦāĻĄāĻŧ āĻ…āĻĒāĻļāύ āĻŦā§āϝāĻžāĻ— āύ⧇āϝāĻŧ (joins, search, permissions, soft deletes, caching)āĨ¤ āϤāĻ–āύ āφāĻĒāύāĻŋ āφāϰ⧇āĻ•āϟāĻŋ ORM āĻŦāĻžāύāĻŋāϝāĻŧ⧇ āĻĢ⧇āϞ⧇āϛ⧇āύāĨ¤\n- āϖ⧁āĻŦ āĻŦ⧁āĻĻā§āϧāĻŋāĻŽāĻžāύ āĻ•āύāĻ¸ā§āĻŸā§āϰ⧇āχāĻ¨ā§āϟāĨ¤ āϝāĻĻāĻŋ āĻĒāĻžāĻ āĻ•āĻĻ⧇āϰ āϟāĻžāχāĻĒ āϏ⧇āϟ āĻĄāĻŋāϕ⧋āĻĄ āĻ•āϰāϤ⧇ āĻšāϝāĻŧ āĻŦ⧁āĻāϤ⧇ āϝ⧇ āĻāĻ•āϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āϕ⧀ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āϟ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇, āϤāĻ–āύ abstraction-āϟāĻŋ āĻ–āϰāĻšā§‡āϰ āĻŦ⧇āĻļāĻŋ āĻšāϝāĻŧ⧇ āϝāĻžāϝāĻŧāĨ¤\n- āχāύāĻĒ⧁āϟ āϟāĻžāχāĻĒāϕ⧇ DB āĻŽāĻĄā§‡āϞ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŽāĻžāύāĻžāĨ¤ āϝāĻ–āύ Create āĻ“ Update āĻāĻ•āχ struct āύ⧇āϝāĻŧ āϝāĻž āφāĻĒāύāĻŋ āϰ⧋ āĻĨ⧇āϕ⧇ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰ⧇āύ, DB āĻĄāĻŋāϜāĻžāχāύ āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻžāϰ āĻ“ āĻŸā§‡āĻ¸ā§āĻŸā§‡ āϞāĻŋāĻ• āĻ•āϰ⧇ āĻāĻŦāĻ‚ schema āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ…ā§āϝāĻžāĻĒ⧇ āĻ›āĻĄāĻŧāĻŋāϝāĻŧ⧇ āĻĒāĻĄāĻŧ⧇āĨ¤\n- List-āĻ āύ⧀āϰāĻŦ⧇ āφāϚāϰāĻŖ: āĻ…āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ sorting, inconsistent defaults, āĻŦāĻž paging āύāĻŋāϝāĻŧāĻŽ āϝāĻž āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻ…āύ⧁āϝāĻžāϝāĻŧā§€ āĻŦāĻĻāϞ⧇ āϝāĻžāϝāĻŧāĨ¤\n\nāĻāĻ•āϟāĻŋ āĻŦāĻžāĻ¸ā§āϤāĻŦ āωāĻĻāĻžāĻšāϰāĻŖ: ListCustomers āĻĒā§āϰāϤāĻŋāĻŦāĻžāϰ āĻ­āĻŋāĻ¨ā§āύ āĻ…āĻ°ā§āĻĄāĻžāϰ⧇ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇ āϝāĻĻāĻŋ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋāϟāĻŋ ORDER BY āύāĻž āĻĻ⧇āϝāĻŧāĨ¤ āϤāĻ–āύ pagination āϰ⧇āĻ•āĻ°ā§āĻĄ āĻĄā§āĻĒā§āϞāĻŋāϕ⧇āϟ āĻŦāĻž āĻ¸ā§āĻ•āĻŋāĻĒ āĻ•āϰ⧇āĨ¤ ordering āĻ¸ā§āĻĒāĻˇā§āϟ āĻ•āϰ⧁āύ (āϝāĻĻāĻŋ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āĻšāϝāĻŧ āĻļ⧁āϧ⧁āχ āĻĒā§āϰāĻžāχāĻŽāĻžāϰāĻŋ āϕ⧀ āĻĻāĻŋāϝāĻŧ⧇) āĻāĻŦāĻ‚ āĻĄāĻŋāĻĢāĻ˛ā§āϟāϗ⧁āϞ⧋ āĻ•āύāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϟ āϰāĻžāϖ⧁āύāĨ¤\n\n## āĻ—ā§āϰāĻšāĻŖ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻĻā§āϰ⧁āϤ āĻšā§‡āĻ•āϞāĻŋāĻ¸ā§āϟ\n\nāĻœā§‡āύ⧇āϰāĻŋāĻ• āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĒā§āϝāĻžāϕ⧇āĻœā§‡ āϰ⧋āϞ āφāωāϟ āĻ•āϰāĻžāϰ āφāϗ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧁āύ āĻāϟāĻž āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ•āĻŽāĻžāĻŦ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āφāϚāϰāĻŖ āϞ⧁āĻ•āĻžāĻŦ⧇ āύāĻžāĨ¤\n\nāĻ•āύāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϏāĻŋ āĻĻāĻŋāϝāĻŧ⧇ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύāĨ¤ āĻāĻ•āϟāĻŋ āϰāĻŋāĻĒā§‹ context.Context āύ⧇āϝāĻŧ āφāϰ āĻ…āĻ¨ā§āϝāϟāĻŋ āύāĻž āϝāĻĻāĻŋ āĻāĻŽāύ āĻšāϝāĻŧ, āĻ…āĻĨāĻŦāĻž āĻāĻ•āϟāĻŋ (T, error) āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇ āφāϰ āĻ…āĻ¨ā§āϝāϟāĻŋ (*T, error) — āĻāϏāĻŦ āĻ…āϏāĻžāĻŽāĻžā§āϜāĻ¸ā§āϝ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ, āĻŸā§‡āĻ¸ā§āϟ, āĻ“ āĻŽāĻ•-āĻ āϏāĻŽāĻ¸ā§āϝāĻž āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤\n\nāĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ āϜāĻ¨ā§āϝ āĻ—āĻ˛ā§āĻĒ⧇ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āĻŦāĻžāĻĄāĻŧāĻŋ āϰāĻžāϖ⧁āύ āϝ⧇āĻ–āĻžāύ⧇ SQL āĻĨāĻžāϕ⧇āĨ¤ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϕ⧋āĻĄ āĻĢā§āϞ⧋ reuse āĻ•āϰ⧁āĻ• (scan, validate, map errors), āĻ•āĻŋāĻ¨ā§āϤ⧁ āϕ⧋āϝāĻŧ⧇āϰāĻŋ āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚ āϟ⧁āĻ•āϰ⧋ āĻ›āĻĄāĻŧāĻŋāϝāĻŧ⧇ āύāĻž āĻĒāĻĄāĻŧ⧁āĻ•āĨ¤\n\nāĻāĻ•āϟāĻŋ āĻĻā§āϰ⧁āϤ āĻšā§‡āϕ⧇āϰ āϏ⧇āϟ āϝāĻž āĻŦ⧇āĻļāĻŋāϰāĻ­āĻžāĻ— āĻŦāĻŋāĻ¸ā§āĻŽāϝāĻŧ āĻĒā§āϰāϤāĻŋāϰ⧋āϧ āĻ•āϰ⧇:\n\n- List/Get/Create/Update/Delete-āĻāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āϏāĻŋāĻ—āύ⧇āϚāĻžāϰ āĻ•āύāϭ⧇āύāĻļāύ\n- āĻĒā§āϰāϤāĻŋāϟāĻŋ āϰāĻŋāĻĒā§‹āϤ⧇ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻāĻ• āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ not-found āύāĻŋāϝāĻŧāĻŽ\n- āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ list ordering āϝāĻž āĻĄāϕ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻĄ āĻ“ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰāĻž āφāϛ⧇\n- *sql.DB āĻ“ *sql.Tx āĻāĻ•āχ āϕ⧋āĻĄā§‡ āϚāĻžāϞāĻžāύ⧋āϰ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āωāĻĒāĻžāϝāĻŧ (executor āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ)\n- āĻœā§‡āύ⧇āϰāĻŋāĻ• āϕ⧋āĻĄ āĻ“ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āύāĻŋāϝāĻŧāĻŽā§‡āϰ āĻŽāĻ§ā§āϝ⧇ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āϏ⧀āĻŽāĻžāύāĻž (āĻ­ā§āϝāĻžāϞāĻŋāĻĄā§‡āĻļāύ āĻ“ āĻŦāĻŋāϜāύ⧇āϏ āĻšā§‡āĻ• āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ¸ā§āϤāϰ⧇āϰ āĻŦāĻžāχāϰ⧇ āϰāĻžāϖ⧁āύ)\n\nāφāĻĒāύāĻŋ āϝāĻĻāĻŋ AppMaster-āĻ āĻĻā§āϰ⧁āϤāχ āĻ…āĻ­ā§āϝāĻ¨ā§āϤāϰ⧀āĻŖ āϟ⧁āϞ āĻŦāĻžāύāĻžāĻšā§āϛ⧇āύ āĻāĻŦāĻ‚ āĻĒāϰ⧇ āĻœā§‡āύāĻžāϰ⧇āĻŸā§‡āĻĄ Go āϕ⧋āĻĄ āĻāĻ•ā§āϏāĻĒā§‹āĻ°ā§āϟ āĻŦāĻž āĻŦ⧃āĻĻā§āϧāĻŋ āĻ•āϰāϛ⧇āύ, āĻāχ āĻšā§‡āĻ•āϗ⧁āϞ⧋ āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰāϕ⧇ āĻĒā§‚āĻ°ā§āĻŦāĻžāύ⧁āĻŽāĻžāύāϝ⧋āĻ—ā§āϝ āĻ“ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰāĻž āϏāĻšāϜ āϰāĻžāĻ–āϤ⧇ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇āĨ¤\n\n## āĻŦāĻžāĻ¸ā§āϤāĻŦ āωāĻĻāĻžāĻšāϰāĻŖ: āĻāĻ•āϟāĻŋ Customer āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰāĻž\n\nāύāĻŋāĻšā§‡ āĻāĻ•āϟāĻŋ āϛ⧋āϟ Customer āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϰ⧂āĻĒ āϝāĻž āϟāĻžāχāĻĒ-āϏ⧇āĻĢ āϰ⧇āϖ⧇ āĻŦ⧇āĻļ āϏāϰāϞāĨ¤\n\nāĻ¸ā§āĻŸā§‹āϰāĻĄ āĻŽāĻĄā§‡āϞ āĻĻāĻŋāϝāĻŧ⧇ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύāĨ¤ ID āĻļāĻ•ā§āϤ āϟāĻžāχāĻĒ āĻ•āϰāĻž āϰāĻžāϖ⧁āύ āϝāĻžāϤ⧇ āϭ⧁āϞ ID āĻŽāĻŋāĻļ⧇ āύāĻž āϝāĻžāϝāĻŧ:\n\ngo\ntype CustomerID int64\n\ntype Customer struct {\n\tID CustomerID\n\tName string\n\tStatus string // "active", "blocked", "trial"...\n}\n\n\nāĻāĻ–āύ “API āĻ•āĻŋ āύ⧇āϝāĻŧ” āĻāĻŦāĻ‚ “āφāĻĒāύāĻŋ āĻ•āĻŋ āĻ¸ā§āĻŸā§‹āϰ āĻ•āϰ⧇āĻ¨â€ āφāϞāĻžāĻĻāĻž āĻ•āϰ⧁āύāĨ¤ Create āĻ“ Update āĻāĻ–āĻžāύ⧇ āφāϞāĻžāĻĻāĻž āĻšāĻ“āϝāĻŧāĻž āωāϚāĻŋāϤāĨ¤\n\ngo\ntype CreateCustomerInput struct {\n\tName string\n\tStatus string\n}\n\ntype UpdateCustomerInput struct {\n\tName *string\n\tStatus *string\n}\n\n\nāφāĻĒāύāĻžāϰ āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻŦ⧇āϏ āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĢā§āϞ⧋ (āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāύ⧋, āĻ¸ā§āĻ•ā§āϝāĻžāύ, āĻāϰāϰ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚) āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰāĻŦ⧇, āφāϰ Customer repo Customer-āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ SQL āĻ“ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻĻ⧇āĻ–āĻŦ⧇āĨ¤ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āĻĻ⧃āĻˇā§āϟāĻŋāϤ⧇ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻĨāĻžāĻ•āĻŦ⧇:\n\ngo\ntype CustomerRepo interface {\n\tCreate(ctx context.Context, in CreateCustomerInput) (Customer, error)\n\tUpdate(ctx context.Context, id CustomerID, in UpdateCustomerInput) (Customer, error)\n\tGet(ctx context.Context, id CustomerID) (Customer, error)\n\tDelete(ctx context.Context, id CustomerID) error\n\tList(ctx context.Context, q CustomerListQuery) ([]Customer, int, error)\n}\n\n\nList-āĻāϰ āϜāĻ¨ā§āϝ, āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ pagination āϕ⧇ āĻĢāĻžāĻ°ā§āĻ¸ā§āϟ-āĻ•ā§āϞāĻžāϏ āĻ…āύ⧁āϰ⧋āϧ āĻ…āĻŦāĻœā§‡āĻ•ā§āϟ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦāĻŋāĻŦ⧇āϚāύāĻž āĻ•āϰ⧁āύāĨ¤ āĻāϟāĻŋ āĻ•āϞ āϏāĻžāχāϟāϗ⧁āϞ⧋āϕ⧇ āĻĒāĻĄāĻŧāϤ⧇ āϏāĻšāϜ āϰāĻžāϖ⧇ āĻāĻŦāĻ‚ āϞāĻŋāĻŽāĻŋāϟ āϭ⧁āϞ⧇ āϝāĻžāĻ“āϝāĻŧāĻž āĻ•āĻ āĻŋāύ āĻ•āϰ⧇ āĻĻ⧇āϝāĻŧāĨ¤\n\ngo\ntype CustomerListQuery struct {\n\tStatus *string // filter\n\tSearch *string // name contains\n\tLimit int\n\tOffset int\n}\n\n\nāĻāĻ–āĻžāύ āĻĨ⧇āϕ⧇ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύāϟāĻŋ āĻ­āĻžāϞ⧋āĻ­āĻžāĻŦ⧇ āĻ¸ā§āϕ⧇āϞ āĻ•āϰ⧇: āĻĒāϰ⧇āϰ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ āϜāĻ¨ā§āϝ āĻāĻ•āχ āĻ¸ā§āĻŸā§āϰāĻžāĻ•āϚāĻžāϰ āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰ⧁āύ, āχāύāĻĒ⧁āϟ āĻ¸ā§āĻŸā§‹āϰāĻĄ āĻŽāĻĄā§‡āϞ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻž āϰāĻžāϖ⧁āύ, āĻāĻŦāĻ‚ āĻ¸ā§āĻ•ā§āϝāĻžāύāĻŋāĻ‚ āĻ¸ā§āĻĒāĻˇā§āϟ āϰāĻžāϖ⧁āύ āϝāĻžāϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϗ⧁āϞ⧋ āĻ¸ā§āĻŦāĻšā§āĻ› āĻ“ āĻ•āĻŽā§āĻĒāĻžāχāϞāĻžāϰ-āϏāĻšāĻžāϝāĻŧāĻŋāϤ āĻĨāĻžāϕ⧇āĨ¤

āĻĒā§āϰāĻļā§āύ⧋āĻ¤ā§āϤāϰ

What problem do generic CRUD repositories in Go actually solve?

āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āφāĻĒāύāĻŋ āϕ⧇āĻŦāϞ āĻĒā§āϰāĻŦāĻžāĻšāϟāĻŋāχ āĻĒ⧁āύāσāĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ (āϕ⧋āϝāĻŧ⧇āϰāĻŋ āϚāĻžāϞāĻžāύ⧋, āϰ⧋ āϞ⧁āĻĒ, not-found āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻŋāĻ‚, pagination āĻĄāĻŋāĻĢāĻ˛ā§āϟ, āĻāϰāϰ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚) — āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ SQL āĻ“ āϰ⧋ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āφāϞāĻžāĻĻāĻž āϰāĻžāĻ–āĻŦ⧇āύāĨ¤ āĻāϤ⧇ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ•āĻŽāĻŦ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰ runtime-āĻ āĻ…āĻĻ⧃āĻļā§āϝ “āĻŽā§āϝāĻžāϜāĻŋāĻ•â€ āĻšāϝāĻŧ⧇ āϝāĻžāĻŦ⧇ āύāĻžāĨ¤

Why avoid reflection-based “scan any struct” CRUD helpers?

āϰāĻŋāĻĢā§āϞ⧇āĻ•āĻļāύ āĻŽā§āϝāĻžāĻĒāĻŋāĻ‚ āύāĻŋāϝāĻŧāĻŽāϗ⧁āϞ⧋ āϞ⧁āĻ•āĻŋāϝāĻŧ⧇ āĻĻ⧇āϝāĻŧ āĻāĻŦāĻ‚ āĻŦā§āϝāĻ°ā§āĻĨāϤāĻž runtime-āĻ āϚāϞ⧇ āϝāĻžāϝāĻŧāĨ¤ āĻ•āĻŽā§āĻĒāĻžāχāϞāĻžāϰ āĻšā§‡āĻ• āϚāϞ⧇ āϝāĻžāϝāĻŧ, IDE āϏāĻšāĻžāϝāĻŧāϤāĻž āĻ•āĻŽā§‡ āϝāĻžāϝāĻŧ, āφāϰ āϛ⧋āϟ schema āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϗ⧁āϞ⧋ āφāϚāĻŽāĻ•āĻž āϏāĻŽāĻ¸ā§āϝāĻžāϰ āĻ•āĻžāϰāĻŖ āĻšāϝāĻŧ⧇ āĻ“āϠ⧇āĨ¤ āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ⧇āϰ āϏāĻ™ā§āϗ⧇ āĻ¸ā§āĻĒāĻˇā§āϟ scanner āĻĢāĻžāĻ‚āĻļāύ āϰāĻžāĻ–āϞ⧇ āϟāĻžāχāĻĒ āϏ⧇āĻĢāϟāĻŋ āĻŦāϜāĻžāϝāĻŧ āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ āĻāĻ•āχ āϏāĻŽāϝāĻŧ⧇ 반ëŗĩ āĻ…āĻ‚āĻļāϗ⧁āϞ⧋ āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤

What’s a sensible constraint for an ID type?

āϏāĻžāϧāĻžāϰāĻŖāϤ comparable āĻ­āĻžāϞ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•āĻžāϰāĻŖ ID-āϗ⧁āϞ⧋ āϤ⧁āϞāύāĻž āĻ•āϰāĻž āĻšāϝāĻŧ, map-āĻāϰ āϕ⧀ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻŦāĻžāϰāĻŦāĻžāϰ āĻĒāĻžāϏ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āϝāĻĻāĻŋ āφāĻĒāύāĻžāϰ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡ āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ ID āĻ¸ā§āϟāĻžāχāϞ āĻĨāĻžāϕ⧇ (āϝ⧇āĻŽāύ int64 āĻ“ UUID string), āϤāĻžāĻšāϞ⧇ ID āϟāĻžāχāĻĒ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϰāĻžāĻ–āĻž āĻ­āĻžāϞ⧋ āϝāĻžāϤ⧇ āĻāĻ•āϟāĻŋ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āϏāĻŦ āϰ⧇āĻĒā§‹āϤ⧇ āĻœā§‹āϰ āύāĻž āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤

What should the entity constraint include (and not include)?

āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϕ⧇āĻŦāϞ āϏ⧇āχ āϜāĻŋāύāĻŋāϏāϗ⧁āϞ⧋āχ āĻĻāĻžāĻŦāĻŋ āĻ•āϰāĻŦ⧇ āϝāĻž āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ CRUD āĻĢā§āϞ⧋-āϕ⧇ āĻĻāϰāĻ•āĻžāϰ, āϏāĻžāϧāĻžāϰāĻŖāϤ GetID() āĻ“ SetID()āĨ¤ struct embedding āĻŦāĻž āϜāϟāĻŋāϞ type-set āĻŸā§āϰāĻŋāĻ•āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦāĻžāĻ§ā§āϝ āĻ•āϰāĻž āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϚāϞ⧁āύ — āϤāĻž āφāĻĒāύāĻžāϰ āĻĄā§‹āĻŽā§‡āχāύ āϟāĻžāχāĻĒāϗ⧁āϞ⧋āϕ⧇ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ⧇ āĻŦ⧇āρāϧ⧇ āĻĻ⧇āĻŦ⧇āĨ¤

How do I support both *sql.DB and *sql.Tx cleanly?

āĻāĻ•āϟāĻŋ āϛ⧋āϟ executor āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ (āϝ⧇āĻŽāύ DBTX) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ āϝāĻžāϤ⧇ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āφāĻĒāύāĻžāϰ āĻĄāĻžāĻ•āĻž āĻŽā§‡āĻĨāĻĄāϗ⧁āϞ⧋ āĻĨāĻžāϕ⧇, āϝ⧇āĻŽāύ QueryContext, QueryRowContext, ExecContextāĨ¤ āϤāĻ–āύ *sql.DB āĻŦāĻž *sql.Tx āϝ⧇ āϕ⧋āύ⧋āϟāĻŋ executor āĻšāĻŋāϏ⧇āĻŦ⧇ āĻĒāĻžāϏ āĻ•āϰāĻž āϝāĻžāĻŦ⧇ āĻāĻŦāĻ‚ āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āϕ⧋āĻĄā§‡ āĻļāĻžāĻ–āĻž-āĻŦāĻŋāĻ­āĻžāĻ— āϝ⧋āĻ— āĻ•āϰāϤ⧇ āĻšāĻŦ⧇ āύāĻžāĨ¤

What’s the best way to signal “not found” from Get?

Get(ctx, id) āϟāĻžāχāĻĒ⧇āĻĄ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻāĻŦāĻ‚ āĻ¸ā§āĻĒāĻˇā§āϟ āĻŽāĻŋāϏāĻŋāĻ‚-āϰ⧇āĻ•āĻ°ā§āĻĄ āϏāĻ‚āϕ⧇āϤ āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧ — āϏāĻžāϧāĻžāϰāĻŖāϤ āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ sentinel āĻāϰāϰ āϝ⧇āĻŽāύ ErrNotFoundāĨ¤ āĻļā§‚āĻ¨ā§āϝ-āĻŽā§‚āĻ˛ā§āϝ āĻāĻ¨ā§āϟāĻŋāϟāĻŋ āĻ“ nil āĻāϰāϰ āĻĢ⧇āϰāϤ āĻĻāĻŋāϞ⧇ āĻ•āϞāĻžāϰāϰāĻž āĻŦ⧁āĻāϤ⧇ āĻĒāĻžāϰ⧇ āύāĻž āϝ⧇ āϰ⧇āĻ•āĻ°ā§āĻĄāϟāĻŋ āύ⧇āχ āύāĻžāĻ•āĻŋ āĻļ⧁āϧ⧁ āĻĢāĻŋāĻ˛ā§āĻĄāϗ⧁āϞ⧋ āĻ–āĻžāϞāĻŋāĨ¤

Should Create/Update take the full entity struct?

āχāύāĻĒ⧁āϟāϗ⧁āϞ⧋āϕ⧇ āĻ¸ā§āĻŸā§‹āϰ āĻ•āϰāĻž āĻŽāĻĄā§‡āϞ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻž āϰāĻžāϖ⧁āύāĨ¤ āĻĒā§āϰāĻžāϝāĻŧāĻļāχ Create(ctx, CreateInput) āĻ“ Update(ctx, id, UpdateInput) āĻŦ⧇āĻļāĻŋ āύāĻŋāϰāĻžāĻĒāĻĻ â€” āĻāϤ⧇ āĻ•āϞāĻžāϰāϰāĻž āϏāĻžāĻ°ā§āĻ­āĻžāϰ-āύāĻŋāϝāĻŧāĻ¨ā§āĻ¤ā§āϰāĻŋāϤ āĻĢāĻŋāĻ˛ā§āĻĄ (ID, timestamps) āύāĻŋāĻœā§‡ āϏ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ āύāĻžāĨ¤ āĻĒā§āϝāĻžāϚ-āĻ¸ā§āϟāĻžāχāϞ āφāĻĒāĻĄā§‡āϟ āĻšāϞ⧇ āĻĒāϝāĻŧ⧇āĻ¨ā§āϟāĻžāϰ āĻŦāĻž nullable āϟāĻžāχāĻĒ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ āϝāĻžāϤ⧇ “āĻ…āϏ⧇āĻŸâ€ āĻ“ “āĻļā§‚āĻ¨ā§āϝāϤ⧇ āϏ⧇āĻŸâ€ āφāϞāĻžāĻĻāĻž āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤

How do I keep List pagination from returning inconsistent results?

āĻĒā§āϰāϤāĻŋāĻŦāĻžāϰ āĻ¸ā§āĻĒāĻˇā§āϟ, āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ ORDER BY āύāĻŋāĻ°ā§āϧāĻžāϰāĻŖ āĻ•āϰ⧁āύ, āϏāĻŽā§āĻ­āĻŦ āĻšāϞ⧇ āχāωāύāĻŋāĻ• āĻ•āϞāĻžāĻŽ (āĻĒā§āϰāĻžāχāĻŽāĻžāϰāĻŋ āϕ⧀) āĻĻāĻŋāϝāĻŧ⧇āĨ¤ āύāĻž āĻšāϞ⧇ āύāϤ⧁āύ āϰ⧇āĻ•āĻ°ā§āĻĄ āĻāϞ⧇ āĻŦāĻž āĻĒā§āĻ˛ā§āϝāĻžāύāĻžāϰ āĻŦāĻĻāϞāĻžāϞ⧇ pagination-āĻ āφāχāĻŸā§‡āĻŽāϗ⧁āϞ⧋ āϞāĻžāĻĢāĻŋāϝāĻŧ⧇ āĻĒāĻĄāĻŧāĻŦ⧇ āĻŦāĻž āĻ•āĻĒāĻŋ/āĻ¸ā§āĻ•āĻŋāĻĒ āĻšāĻŦ⧇āĨ¤

What error contract should repositories provide to services?

āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĨ⧇āϕ⧇ āϏāĻžāĻ°ā§āĻ­āĻŋāϏāϗ⧁āϞ⧋āϕ⧇ āϝ⧇ āĻāϰāϰāϗ⧁āϞ⧋āϤ⧇ āĻļāĻžāĻ–āĻž āĻ•āϰāĻž āωāϚāĻŋāϤ āϤāĻž āϏ⧀āĻŽāĻŋāϤ āϰāĻžāϖ⧁āύ, āϝ⧇āĻŽāύ ErrNotFound āĻ“ ErrConflictāĨ¤ āĻŦāĻžāĻ•āĻŋ āϏāĻŦāĻ•āĻŋāϛ⧁ low-level āĻāϰāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ context-āϏāĻš wrap āĻ•āϰ⧁āύāĨ¤ āĻ•āϞāĻžāϰāϰāĻž string parse āĻ•āϰ⧇ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύ⧇āĻŦ⧇ āύāĻž — errors.Is āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻļāĻžāĻ–āĻž āĻ•āϰāĻž āϝāĻžāĻŦ⧇āĨ¤

How should I test a generic repository pattern without over-testing it?

āĻļ⧇āϝāĻŧāĻžāĻ°ā§āĻĄ āĻšā§‡āĻ˛ā§āĻĒāĻžāϰāϗ⧁āϞ⧋ āĻāĻ•āĻŦāĻžāϰ āχāωāύāĻŋāϟ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧁āύ (pagination normalization, not-found mapping, affected-row checks) āĻāĻŦāĻ‚ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻāĻ¨ā§āϟāĻŋāϟāĻŋāϰ SQL āĻ“ scanning āφāϞāĻžāĻĻāĻž āĻ•āϰ⧇ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧁āύāĨ¤ āϛ⧋āϟ contract tests āϰāĻžāϖ⧁āύ: create→get āĻŽāĻŋāϞāϛ⧇, update āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦāĻĻāϞāĻžāϝāĻŧ, delete āĻĒāϰ⧇ ErrNotFound āφāϏ⧇, list ordering āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āχāĻ¤ā§āϝāĻžāĻĻāĻŋāĨ¤

āĻļ⧁āϰ⧁ āĻ•āϰāĻž āϏāĻšāϜ
āĻ•āĻŋāϛ⧁ āφāĻļā§āϚāĻ°ā§āϝāϜāύāĻ•āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

āĻŦāĻŋāύāĻžāĻŽā§‚āĻ˛ā§āϝ⧇āϰ āĻĒāϰāĻŋāĻ•āĻ˛ā§āĻĒāύāĻž āϏāĻš āĻ…ā§āϝāĻžāĻĒāĻŽāĻžāĻ¸ā§āϟāĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧁āύāĨ¤
āφāĻĒāύāĻŋ āϝāĻ–āύ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻšāĻŦ⧇āύ āϤāĻ–āύ āφāĻĒāύāĻŋ āϏāĻ āĻŋāĻ• āϏāĻĻāĻ¸ā§āϝāϤāĻž āĻŦ⧇āϛ⧇ āύāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āύ⧎

āĻāĻŦāĻžāϰ āĻļ⧁āϰ⧁ āĻ•āϰāĻž āϝāĻžāĻ•
āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ Go āĻĄā§‡āϟāĻž āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ Go āĻœā§‡āύ⧇āϰāĻŋāĻ•āϏ CRUD āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ | AppMaster