ਸਿੱਖੋ ਕਿ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਟੀਮਾਂ ਨੂੰ ਕਿਵੇਂ ਸਪਸ਼ਟ ਮੋਡਿਊਲ, مਿਡਲਵੇਅਰ ਅਤੇ ਸੀਮਾਵਾਂ ਨਾਲ ਕਸਟਮ ਆਰਕੀਟੈਕਚਰ ਬਣਾਉਣ ਦਿੰਦੇ ਹਨ—ਨਾਲ ਹੀ ਟਰੇਡ-ਆਫ, ਪੈਟਰਨ ਅਤੇ ਜ਼ਰੂਰੀ ਧਿਆਨ-ਬਿੰਦੂ।

ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਹਲਕੇ ਫ਼੍ਰੇਮਵਰਕ ਹੁੰਦੇ ਹਨ ਜੋ ਆਮ ਤੌਰ 'ਤੇ ਬੁਨਿਆਦੀ ਗੱਲਾਂ 'ਤੇ ਧਿਆਨ ਦਿੰਦੇ ਹਨ: ਇਕ ਰਿਕਵੇਸਟ ਲੈਣਾ, ਉਸਨੂੰ ਸਹੀ ਹੈਂਡਲਰ ਨੂੰ ਰਾਊਟ ਕਰਨਾ, ਅਤੇ ਜਵਾਬ ਵਾਪਸ ਭੇਜਣਾ। ਪੂਰੇ-ਸਟੈਕ ਫ਼્રੇਮਵਰਕਾਂ ਦੀ ਤਰ੍ਹਾਂ ਇਨ੍ਹਾਂ ਵਿੱਚ ਆਮ ਤੌਰ 'ਤੇ ਸਭ ਕੁਝ (admin ਪੈਨਲ, ORM/ਡੇਟਾਬੇਸ ਲੇਅਰ, ਫਾਰਮ ਬਿਲਡਰ, ਬੈਕਗ੍ਰਾਊਂਡ ਜ਼ਾਬ) ਨਹੀਂ ਹੁੰਦਾ। ਇਸ ਦੀ ਥਾਂ, ਇਹ ਇੱਕ ਛੋਟਾ, ਸਥਿਰ ਕੋਰ ਦਿੰਦੇ ਹਨ ਅਤੇ ਤੁਸੀਂ ਉਹੀ ਜੋੜਦੇ ਹੋ ਜੋ ਤੁਹਾਨੂੰ ਲੋੜ ਹੈ।
ਇੱਕ ਫੁੱਲ-ਸਟੈਕ ਫ੍ਰੇਮਵਰਕ ਇੱਕ ਤਰ੍ਹਾਂ ਦੀ ਫਰਨੀਸ਼ਡ ਘਰ ਖਰੀਦਣ ਵਰਗਾ ਹੈ: ਸੁਵਿਧਾਜਨਕ ਪਰ ਮੁੜ-ਬਦਲਣਾ ਔਖਾ। ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਇਕ ਖ਼ਾਲੀ ਪਰ ਢਾਂਚਾਗਤ ਤੌਰ 'ਤੇ ਠੀਕ ਜਗ੍ਹਾ ਵਰਗਾ ਹੈ: ਤੁਸੀਂ ਕਮਰੇ, ਫਰਨੀਚਰ ਅਤੇ ਯੂਟਿਲਿਟੀ ਫੈਸਲੇ ਕਰਦੇ ਹੋ।
ਇਹ ਆਜ਼ਾਦੀ ਹੀ ਉਹ ਹੈ ਜੋ ਅਸੀਂ ਕਸਟਮ ਆਰਕੀਟੈਕਚਰ ਕਹਿੰਦੇ ਹਾਂ—ਇੱਕ ਸਿਸਟਮ ਡਿਜ਼ਾਇਨ ਜੋ ਤੁਹਾਡੇ ਟੀਮ ਦੀਆਂ ਜ਼ਰੂਰਤਾਂ, ਤੁਹਾਡੇ ਡੋਮੇਨ ਅਤੇ ਓਪਰੇਸ਼ਨਲ ਪਾਬੰਦੀਆਂ ਮੁਤਾਬਕ ਹੋਵੇ। ਸਧਾਰਨ ਸ਼ਬਦਾਂ ਵਿੱਚ: ਤੁਸੀਂ ਕੰਪੋਨੈਂਟਾਂ (ਲੌਗਿੰਗ, ਡੇਟਾਬੇਸ ਐਕਸਿਸ, ਵੈਲੀਡੇਸ਼ਨ, auth, ਬੈਕਗ੍ਰਾਊਂਡ ਪ੍ਰੋਸੈਸਿੰਗ) ਚੁਣਦੇ ਹੋ ਅਤੇ ਇਹ ਫੈਸਲਾ ਕਰਦੇ ਹੋ ਕਿ ਇਹ ਕਿਵੇਂ ਜੁੜਦੇ ਹਨ, ਨਾ ਕਿ ਇੱਕ ਪਹਿਲਾਂ-ਨਿਰਧਾਰਿਤ "ਇਕ ਸahi ਰਾਹ" ਨੂੰ ਮਨਜ਼ੂਰ ਕਰਨਾ।
ਟੀਮਾਂ ਅਕਸਰ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਨੂੰ ਇਨ੍ਹਾਂ ਕਾਰਨਾਂ ਕਰਕੇ ਚੁਣਦੀਆਂ ਹਨ:
ਅਸੀਂ ਇਸ ਗੱਲ 'ਤੇ ਧਿਆਨ ਦੇਵਾਂਗੇ ਕਿ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਕਿਵੇਂ ਮੋਡਿਊਲਰ ਡਿਜ਼ਾਈਨ ਨੂੰ ਸਹਾਰਦੇ ਹਨ: ਬਣਾਉਂਦੇ ਬਲਾਕਾਂ ਨੂੰ ਜੋੜਨਾ, middleware ਵਰਤਣਾ, ਅਤੇ dependency injection ਜੋ ਪ੍ਰੋਜੈਕਟ ਨੂੰ ਇੱਕ ਵਿਗਿਆਨ ਪ੍ਰਯੋਗ ਨਾ ਬਣਾਏ।
ਅਸੀਂ ਕਿਸੇ ਖਾਸ ਫ਼੍ਰੇਮਵਰਕ ਦੀ ਲਾਈਨ-ਬਾਈ-ਲਾਈਨ ਤੁਲਨਾ ਨਹੀਂ ਕਰਾਂਗੇ ਅਤੇ ਨਾ ਹੀ ਦਾਵਾ ਕਰਾਂਗੇ ਕਿ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਸਦਾ ਬੇਹਤਰ ਹਨ। ਮਕਸਦ ਇਹ ਹੈ ਕਿ ਤੁਸੀਂ ਸੰਰਚਨਾ ਸੂਚਿਤ ਤਰੀਕੇ ਨਾਲ ਚੁਣੋ—ਅਤੇ ਜਦੋਂ ਲੋੜ ਬਦਲੇ ਤਾਂ ਸੁਰੱਖਿਅਤ ਤਰੀਕੇ ਨਾਲ ਵਧਾਓ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਸਭ ਤੋਂ ਚੰਗੇ ਤਦ ਹੋਂਦੇ ਹਨ ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਅਪਲੀਕੇਸ਼ਨ ਨੂੰ ਇੱਕ ਕਿਟ ਵਾਂਗ ਸਮਝੋ, ਨਾ ਕਿ ਇਕ ਪਹਿਲਾਂ-ਬਣੀ ਘਰ। ਇੱਕ ਅਨੁਮਤ ਸਟੈਕ ਨੂੰ ਮੰਨਣ ਦੀ ਥਾਂ, ਤੁਸੀਂ ਇੱਕ ਛੋਟੇ ਕੋਰ ਨਾਲ ਸ਼ੁਰੂ ਕਰਦੇ ਹੋ ਅਤੇ ਉਹੀ ਸਮਰਥਾ ਜੋ ਲਾਭਦਾਇਕ ਹੋਵੇ ਜੋੜਦੇ ਹੋ।
ਇੱਕ ਵਿਹਤਰੀ "ਕੋਰ" ਆਮ ਤੌਰ 'ਤੇ ਕੇਵਲ ਇਹ ਹੁੰਦਾ ਹੈ:
ਇਹ ਇੱਕ ਕੰਮ ਕਰਨ ਵਾਲਾ API ਐਂਡਪੋਇੰਟ ਜਾਂ ਵੈਬ ਪੇਜ ਸ਼ਿੱਪ ਕਰਨ ਲਈ ਕਾਫੀ ਹੈ। ਹਰ ਹੋਰ ਚੀਜ਼ ਤੁਸੀਂ ਉਸ ਵੇਲੇ ਤੱਕ ਨਹੀਂ ਜੋੜਦੇ ਜਦੋਂ ਤਕ ਸਪਸ਼ਟ ਕਾਰਨ ਨਾ ਹੋਵੇ।
ਜਦੋਂ ਤੁਹਾਨੂੰ authentication, validation ਜਾਂ logging ਦੀ ਲੋੜ ਹੋਵੇ, ਉਨ੍ਹਾਂ ਨੂੰ ਵੱਖ-ਵੱਖ ਕੰਪੋਨੈਂਟ ਵਜੋਂ ਜੋੜੋ—ਸਭ ਤੋਂ ਵਧੀਆ ਇਹ ਹੈ ਕਿ ਝਿਲੀ ਹੋਈਆਂ ਇੰਟਰਫੇਸਾਂ ਦੇ ਪਿੱਛੇ ਰੱਖੋ। ਇਸ ਨਾਲ ਤੁਹਾਡੀ ਆਰਕੀਟੈਕਚਰ ਸਮਝਣ ਯੋਗ ਰਹਿੰਦੀ ਹੈ: ਹਰ ਨਵਾਂ ਹਿੱਸਾ ਇਹ ਜਵਾਬ ਦੇਣਾ ਚਾਹੀਦਾ ਹੈ "ਇਹ ਕਿਹੜਾ ਸਮੱਸਿਆ ਹੱਲ ਕਰਦਾ ਹੈ?" ਅਤੇ "ਇਹ ਕਿੱਥੇ ਪਲੱਗ-ਇਨ ਹੁੰਦਾ ਹੈ?"
ਫੀਚਰਸ ਉਦਾਹਰਨ:
ਸ਼ੁਰੂ ਵਿੱਚ, ਉਹ ਹੱਲ ਚੁਣੋ ਜੋ ਤੁਹਾਨੂੰ ਫਸਾਉਂਦੇ ਨਾ ਹੋਣ। ਬਹੁਤ ਜ਼ਿਆਦਾ ਫਰੇਮਵਰਕ-ਮੈਜਿਕ ਦੇ ਬਦਲੇ ਪਤਲੇ ਰੈਪਰ ਅਤੇ ਕਨਫਿਗਰੇਸ਼ਨ ਨੂੰ ਤਰਜੀਹ ਦਿਓ। ਜੇ ਤੁਸੀਂ ਇੱਕ ਮੋਡਿਊਲ ਬਿਨਾਂ ਬਿਜ਼ਨਸ ਲਾਜਿਕ ਨੂੰ ਦੁਬਾਰਾ ਲਿਖੇ ਬਦਲ ਸਕਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਸਹੀ ਤਰੀਕੇ ਤੇ ਹੋ।
ਆਰਕੀਟੈਕਚਰ ਚੋਣਾਂ ਲਈ ਇੱਕ ਸਧਾਰਨ definition of done: ਟੀਮ ਹਰ ਮੋਡਿਊਲ ਦਾ ਮਕਸਦ ਵਿਆਖਿਆ ਕਰ ਸਕਦੀ, ਉਸਨੂੰ ਇੱਕ-ਦੋ ਦਿਨਾਂ ਵਿੱਚ ਬਦਲ ਸਕਦੀ, ਅਤੇ ਇੱਕਲ-ਸਵਤੰਤਰ ਤੌਰ 'ਤੇ ਟੈਸਟ ਕਰ ਸਕਦੀ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਡਿਜ਼ਾਈਨ ਦੁਆਰਾ ਛੋਟੇ ਰਹਿੰਦੇ ਹਨ, ਜਿਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਤੁਸੀਂ ਆਪਣੀ ਐਪ ਦੇ "ਅੰਗ" ਚੁਣ ਸਕਦੇ ਹੋ ਨਾਕਿ ਇਕ ਪੂਰੇ ਸਰੀਰ ਨੂੰ ਵਿਰਾਸਤ ਵਿੱਚ ਲੈ ਰਹੇ ਹੋ। ਇਹੀ ਚੀਜ਼ ਕਸਟਮ ਆਰਕੀਟੈਕਚਰ ਨੂੰ ਹਕੀਕਤ ਬਣਾਉਂਦੀ: ਤੁਸੀਂ ਘੱਟ ਤੋਂ ਸ਼ੁਰੂ ਕਰ ਸਕਦੇ ਹੋ, ਫਿਰ ਉਹ ਹਿੱਸੇ ਜੋ ਸੱਚਮੁਚ ਲੋੜ ਹੋਣ ਜੋੜੋ।
ਜ਼ਿਆਦਾਤਰ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਆਧਾਰਿਤ ਐਪ ਇਕ ਰਾਊਟਰ ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ ਜੋ URLs ਨੂੰ controllers (ਜਾਂ ਸਧਾਰਨ ਹੈਂਡਲਰ) ਨਾਲ ਜੋੜਦੀ ਹੈ। ਕੰਟਰੋਲਰਾਂ ਨੂੰ ਫੀਚਰ (billing, accounts) ਜਾਂ ਇੰਟਰਫੇਸ (web vs API) ਅਨੁਸਾਰ ਅਯੋਜਿਤ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।
ਮਿਡਲਵੇਅਰ ਆਮ ਤੌਰ 'ਤੇ ਰਿਕਵੇਸਟ/ਰਿਸਪਾਂਸ ਫਲੋ ਨੂੰ ਵ੍ਰੈਪ ਕਰਦਾ ਹੈ ਅਤੇ ਕ੍ਰਾਸ-ਕੱਟਿੰਗ ਚਿੰਤਾਵਾਂ ਲਈ ਸਭ ਤੋਂ ਚੰਗੀ ਥਾਂ ਹੁੰਦਾ ਹੈ:
ਕਿਉਂਕਿ ਮਿਡਲਵੇਅਰ ਕੰਪੋਜ਼ੇਬਲ ਹੁੰਦਾ ਹੈ, ਤੁਸੀਂ ਇਸਨੂੰ ਗਲੋਬਲ ਤੌਰ 'ਤੇ ਲਗਾ ਸਕਦੇ ਹੋ (ਹਰ ਚੀਜ਼ ਨੂੰ ਲੌਗਿੰਗ ਦੀ ਲੋੜ ਹੈ) ਜਾਂ ਕਿਸੇ ਖਾਸ ਰੂਟ ਲਈ ਹੀ ਲਗਾ ਸਕਦੇ ਹੋ (admin endpoints ਲਈ ਸਖ਼ਤ auth)।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਆਮ ਤੌਰ 'ਤੇ ਡੇਟਾ ਲੇਅਰ ਨਚਾਹੁੰਦੇ ਹਨ, ਇਸ ਲਈ ਤੁਸੀਂ ਉਹ ਚੁਣ ਸਕਦੇ ਹੋ ਜੋ ਤੁਹਾਡੀ ਟੀਮ ਅਤੇ ਕਾਰਜਭਾਰ ਨਾਲ ਮੇਲ ਖਾਂਦਾ ਹੋ:
ਇਕ ਵਧੀਆ ਰੀਤੀ ਇਹ ਹੈ ਕਿ ਡੇਟਾ ਐਕਸੈਸ ਨੂੰ repository ਜਾਂ service ਲੇਅਰ ਦੇ ਪਿੱਛੇ ਰੱਖੋ, ਤਾਂ ਜੋ ਬਾਅਦ ਵਿੱਚ ਟੂਲ ਬਦਲਣ 'ਤੇ ਤੁਹਾਡੇ ਹੈਂਡਲਰ ਵਿੱਚ ਫੈਲਾਅ ਨਾ ਹੋਵੇ।
ਹਰਉਤਪਾਦ ਨੂੰ ਪਹਿਲੇ ਦਿਨ ਤੋਂ async ਪ੍ਰੋਸੈਸਿੰਗ ਦੀ ਲੋੜ ਨਹੀਂ ਹੁੰਦੀ। ਜਦੋਂ ਲੋੜ ਹੋਵੇ, ਇੱਕ job runner ਅਤੇ queue ਜੋੜੋ (ਈਮੇਲ ਭੇਜਣਾ, ਵੀਡੀਓ ਪ੍ਰੋਸੈਸਿੰਗ, webhook). ਬੈਕਗ੍ਰਾਊਂਡ ਜੌਬਸ ਨੂੰ ਤੁਹਾਡੇ ਡੋਮੇਨ ਲਾਜਿਕ ਲਈ ਇਕ ਵੱਖਰਾ “ਐਂਟਰੀ ਪੁਆਇੰਟ” ਸਮਝੋ, ਜੋ HTTP ਲੇਅਰ ਵਰਗੀਆਂ ਹੀ ਸੇਵਾਵਾਂ ਸਾਂਝੀਆਂ ਕਰਨ ਅਤੇ ਨਿਯਮ ਡੁplikੇਟ ਨਾ ਕਰਨ।
ਮਿਡਲਵੇਅਰ ਉਹ ਥਾਂ ਹੈ ਜਿੱਥੇ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਲਾਭ ਦਿੰਦੇ ਹਨ: ਇਹ ਤੁਹਾਨੂੰ ਕ੍ਰਾਸ-ਕੱਟਿੰਗ ਜਰੂਰਤਾਂ (ਜੋ ਹਰ ਰਿਕਵੇਸਟ ਨੂੰ ਮਿਲਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ) ਨੂੰ ਹੈਂਡਲ ਕਰਨ ਦਿੰਦਾ ਹੈ ਬਿਨਾਂ ਹਰ ਰੂਟ ਹੈਂਡਲਰ ਨੂੰ ਫੁੱਲ ਕਰਨ ਦੇ। ਮਕਸਦ ਸਧਾਰਨ ਹੈ: ਹੈਂਡਲਰਾਂ ਨੂੰ ਬਿਜ਼ਨਸ ਲਾਜਿਕ 'ਤੇ ਕੇਂਦਰਤ ਰੱਖੋ, ਅਤੇ ਮਿਡਲਵੇਅਰ ਪਲੰਬਿੰਗ ਸੰਭਾਲੇ।
ਹਰ ਐਂਡਪੋਇੰਟ ਵਿੱਚ ਇੱਕੋ-ਜੇਹੇ ਚੈੱਕ ਅਤੇ ਹੈਡਰ ਦੋਹਰਾਉਣ ਦੀ ਥਾਂ, ਇਕ ਵਾਰੀ ਮਿਡਲਵੇਅਰ ਜੋੜੋ। ਇੱਕ ਸਾਫ਼ ਹੈਂਡਲਰ ਫਿਰ ਇਸ ਤਰ੍ਹਾਂ ਦਿੱਸ ਸਕਦਾ ਹੈ: ਇਨਪੁਟ ਪਾਰਸ ਕਰੋ, ਇੱਕ ਸੇਵਾ ਨੂੰ ਕਾਲ ਕਰੋ, ਰਿਸਪਾਂਸ ਵਾਪਸ ਕਰੋ। ਬਾਕੀ ਧੰਧੇ—auth, logging, validation defaults, response formatting—ਪਹਿਲਾਂ ਜਾਂ ਬਾਅਦ ਵਿੱਚ ਹੋ ਸਕਦੇ ਹਨ।
ਕ੍ਰਮ ਦਾ ਅਰਥ ਹੈ ਵਰਤਾਰਾ। ਇੱਕ ਆਮ, ਪਾਠਯੋਗ ਕ੍ਰਮ ਇਹ ਹੈ:
ਜੇ compression ਬਹੁਤ ਪਹਿਲਾਂ ਚੱਲੀ, ਤਾਂ ਇਹ errors ਨੂੰ ਛੱਡ ਸਕਦੀ ਹੈ; ਜੇ error handling ਦੇਰ ਨਾਲ ਚੱਲੀ, ਤਾਂ ਤੁਸੀਂ stack traces ਲੀਕ ਜਾਂ inconsistent formats ਵਾਪਸ ਕਰ ਸਕਦੇ ਹੋ।
X-Request-Id ਹੈਡਰ ਜੋੜੋ ਅਤੇ ਇਸਨੂੰ ਲੌਗਾਂ ਵਿੱਚ ਸ਼ਾਮِل ਕਰੋ।{ error, message, requestId }).ਮਿਡਲਵੇਅਰ ਨੂੰ ਉਦਦੇਸ਼ ਅਨੁਸਾਰ ਗਰੁੱਪ ਕਰੋ (observability, security, parsing, response shaping) ਅਤੇ ਇਸਨੂੰ ਠੀਕ ਸਕੋਪ 'ਤੇ ਲਗਾਓ: ਸਚਮੁਚ ਸਾਰਵਜਨਕ ਨਿਯਮਾਂ ਲਈ global, ਅਤੇ ਖਾਸ ਖੇਤਰਾਂ ਲਈ route-group middleware (ਜਿਵੇਂ /admin). ਹਰ ਮਿਡਲਵੇਅਰ ਨੂੰ ਸਾਫ਼ ਨਾਂ ਦਿਓ ਅਤੇ setup ਕੋਡ ਦੇ ਨੇੜੇ ਇੱਕ ਛੋਟਾ ਟਿੱਪਣੀ ਵਿੱਚ ਉਮੀਦ ਕੀਤੀ ਕ੍ਰਮ ਦਸਟਾਵੇਜ਼ ਕਰੋ ਤਾਂ ਜੋ ਭਵਿੱਖ ਦੇ ਬਦਲਾਅ ਗੁਪਤ ਤਰੀਕੇ ਨਾਲ ਵਰਤਾਰਾ ਨਾ ਤਬਦੀਲ ਕਰਨ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਨੂੰ ਇਕ ਪਤਲਾ "ਰੀਕਵੇਸਟ-ਇਨ, ਰਿਸਪਾਂਸ-ਆਉਟ" ਕੋਰ ਦਿੰਦਾ ਹੈ। ਬਾਕੀ ਸਭ—ਡੇਟਾਬੇਸ, cache, email, ਤੀਜੀ-ਪੱਖੀ APIs—ਉਸ ਤਰ੍ਹਾਂ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ ਜੋ ਬਦਲੇ ਜਾ ਸਕਣ। ਇੱਥੇ Inversion of Control (IoC) ਅਤੇ Dependency Injection (DI) ਮਦਦ ਕਰਦੇ ਹਨ, ਬਿਨਾਂ ਤੁਹਾਡੀ ਕੋਡਬੇਸ ਨੂੰ ਜ਼ਿਆਦਾ ਜਟਿਲ ਬਣਾਏ।
ਜੇ ਕੋਈ ਫੀਚਰ DB ਦੀ ਲੋੜ ਰੱਖਦਾ ਹੈ, ਤਾਂ ਅੰਦਰੋਂ ਨਵਾਂ client ਬਣਾਉਣ ਦਾ ਪ੍ਰਵਣਤਾ ਹੁੰਦੀ ਹੈ। ਨੁਕਸਾਨ ਇਹ ਹੈ ਕਿ ਹਰ ਜਗ੍ਹਾ ਜੋ "ਸ਼ਾਪਿੰਗ" ਕਰਦੀ ਹੈ, ਉਹ ਇਸ ਖਾਸ DB client ਨਾਲ ਜ਼ਿਆਦਾ ਜੁੜ ਜਾਂਦੀ ਹੈ।
IoC ਇਸਨੂੰ ਉਲਟ ਕਰਦਾ ਹੈ: ਤੁਹਾਡਾ ਫੀਚਰ ਉਹ ਮੰਗਦਾ ਹੈ ਜੋ ਉਸਨੂੰ ਚਾਹੀਦਾ ਹੈ, ਅਤੇ ਐਪ ਦੀ ਤਾਰ-ਤੌਰ ਉਹ ਹੱਥ ਵਿੱਚ ਦਿੰਦੀ ਹੈ। ਤੁਹਾਡਾ ਫੀਚਰ ਦੁਬਾਰਾ ਵਰਤਣਯੋਗ ਅਤੇ ਬਦਲਣਾ ਆਸਾਨ ਬਣ ਜਾਂਦਾ ਹੈ।
Dependency Injection ਦਾ ਸਰਲ ਅਰਥ ਹੈ ਕਿ dependencies ਨੂੰ ਬਣਾਉਣ ਦੀ ਬਜਾਏ ਪਾਸ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਸੈਟਅਪ ਵਿੱਚ ਇਹ ਆਮ ਤੌਰ 'ਤੇ startup 'ਤੇ ਕੀਤਾ ਜਾਂਦਾ ਹੈ:
ਤੁਹਾਨੂੰ ਫਾਇਦਾ ਲੈਣ ਲਈ ਵੱਡਾ DI container ਦੀ ਲੋੜ ਨਹੀਂ। ਇੱਕ ਸਧਾਰਨ ਨਿਯਮ: dependencies ਇਕੋ ਥਾਂ ਬਣਾਓ, ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਹੇਠਾਂ ਪਾਸ ਕਰੋ।
ਕੰਪੋਨੈਂਟਸ ਨੂੰ ਬਦਲ-ਯੋਗ ਬਣਾਉਣ ਲਈ, "ਜੋ ਤੁਹਾਨੂੰ ਚਾਹੀਦਾ ਹੈ" ਨੂੰ ਇਕ ਛੋਟੀ ਇੰਟਰਫੇਸ ਵਜੋਂ ਪਰਿਭਾਸ਼ਤ ਕਰੋ, ਫਿਰ ਖਾਸ ਟੂਲਾਂ ਲਈ ਐਡਾਪਟਰ ਲਿਖੋ।
ਉਦਾਹਰਨ ਪੈਟਰਨ:
UserRepository (interface): findById, create, listPostgresUserRepository (adapter): Postgres ਵਰਤ ਕੇ ਇਹ methods ਨਿਭਾਂਉਂਦਾ ਹੈInMemoryUserRepository (adapter): tests ਲਈਤੁਹਾਡੀ ਬਿਜ਼ਨਸ ਲਾਜਿਕ ਸਿਰਫ UserRepository ਬਾਰੇ ਜਾਣਦੀ ਹੈ, Postgres ਬਾਰੇ ਨਹੀਂ। ਸਟੋਰੇਜ ਬਦਲਣਾ ਇੱਕ configuration ਚੋਣ ਬਣ ਜਾਂਦੀ ਹੈ, rewrite ਨਹੀਂ।
ਇਹੀ ਵਿਚਾਰ external APIs ਲਈ ਵੀ ਲਾਗੂ ਹੁੰਦਾ ਹੈ:
PaymentsGateway interfaceStripePaymentsGateway adapterFakePaymentsGateway локਲ ਵਿਕਾਸ ਲਈਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕਸ ਅਸਾਨੀ ਨਾਲ ਕਨਫਿਗਰੇਸ਼ਨ ਨੂੰ modules ਵਿੱਚ ਫੈਲਵਾ ਸਕਦੇ ਹਨ। ਇਸ ਤੋਂ ਬਚੋ।
ਇੱਕ ਬਣਿਆ pattern:
ਇਸ ਨਾਲ ਤੁਸੀਂ ਮਕਸਦ ਪ੍ਰਾਪਤ ਕਰਦੇ ਹੋ: ਕੰਪੋਨੈਂਟਸ ਨੂੰ ਬਿਨਾਂ ਐਪ ਦੁਬਾਰਾ ਲਿਖੇ ਬਦਲੋ। ਡੇਟਾਬੇਸ ਬਦਲਨਾ, API client ਤਬਦੀਲ ਕਰਨਾ, ਜਾਂ queue ਸ਼ਾਮਿਲ ਕਰਨਾ wiring layer ਵਿੱਚ ਛੋਟਾ ਬਦਲ ਹੁੰਦਾ ਹੈ—ਜਦਕਿ ਬਾਕੀ ਕੋਡ ਸਥਿਰ ਰਹਿੰਦਾ ਹੈ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਡੇ ਕੋਡ ਬਣਤਰ ਲਈ "ਇੱਕ ਸਹੀ ਰਾਹ" ਨੈਹੀ ਦਿੰਦੇ। ਇਸ ਦੀ ਥਾਂ, ਇਹ ਰਾਊਟਿੰਗ, ਰਿਕਵੇਸਟ/ਰਿਸਪਾਂਸ ਹੈਂਡਲਿੰਗ, ਅਤੇ ਕੁਝ ਇਕਸਾਈਟੈਂਸ਼ਨ-ਪੁਆਇੰਟ ਦਿੰਦੇ ਹਨ—ਤਾਂ ਜੋ ਤੁਸੀਂ ਆਪਣੇ ਟੀਮ ਆਕਾਰ, ਉਤਪਾਦ ਪਰਉ ਛੇ, ਅਤੇ ਬਦਲਾਅ ਦੀ ਰਫ਼ਤਾਰ ਅਨੁਸਾਰ ਪੈਟਰਨ ਅਪਣਾ ਸਕੋ।
ਇਹ ਪਰਚਲੇ "ਸਾਫ਼ ਅਤੇ ਸਧਾਰਨ" ਸੈਟਅਪ ਹੈ: controllers HTTP ਸੰਬੰਧੀ ਗੱਲਾਂ ਸੰਭਾਲਦੇ ਹਨ, services ਬਿਜ਼ਨਸ ਰੂਲ ਰੱਖਦੇ ਹਨ, ਅਤੇ repositories DB ਨਾਲ ਗੱਲ ਕਰਦੇ ਹਨ।
ਜਦੋਂ ਤੁਹਾਡਾ ਡੋਮੇਨ ਸਿੱਧਾ ਹੋਵੇ, ਟੀਮ ਛੋਟੀ-ਦਰਮਿਆਨੀ ਹੋ, ਅਤੇ ਤੁਸੀਂ ਕੋਡ ਰੱਖਣ ਲਈ ਪੇਟੇ ਥਾਂ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਇਹ ਚੰਗਾ ਫਿੱਟ ਹੁੰਦਾ ਹੈ। ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਇਸਨੂੰ ਕੁਦਰਤੀ ਤੌਰ 'ਤੇ ਸਹਾਰਦੇ ਹਨ: routes controllers ਨਾਲ ਮਿਲਦੇ ਹਨ, controllers services ਨੂੰ ਕਾਲ ਕਰਦੇ ਹਨ, ਅਤੇ repositories ਹੱਲ ਕਰਕੇ manual composition ਨਾਲ ਵਾਇਰ ਕੀਤੇ ਜਾਂਦੇ ਹਨ।
Hexagonal architecture ਉਸ ਵੇਲੇ ਲਾਭਦਾਇਕ ਹੁੰਦੀ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਅਪੇਕਸ਼ਾ ਕਰੋ ਕਿ ਤੁਹਾਡੀ ਸਿਸਟਮ ਅੱਜ ਦੇ ਚੋਣਾਂ ਤੋਂ ਜ਼ਿਆਦਾ ਲੰਮੇ ਸਮੇਂ ਤੱਕ ਚੱਲੇਗੀ—DB, message bus, ਤੀਜੀ-ਪੱਖੀ APIs, ਜਾਂ UI ਵੀ ਬਦਲ ਸਕਦੇ ਹਨ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਇੱਥੇ ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਕਰਦੇ ਹਨ ਕਿਉਂਕਿ "adapter" ਲੇਅਰ ਅਕਸਰ ਤੁਹਾਡੇ HTTP handlers ਹੁੰਦੇ ਹਨ ਜੋ ਡੋਮੇਨ ਕਮਾਂਡਾਂ 'ਚ ਇਕ ਪਤਲਾ ਤਬਦੀਲ ਕਰਦੇ ਹਨ। ਤੁਹਾਡੇ ports ਡੋਮੇਨ ਇੰਟਰਫੇਸ ਹਨ, ਅਤੇ adapters ਉਨ੍ਹਾਂ ਨੂੰ ਨਿਭਾਉਂਦੇ ਹਨ (SQL, REST clients, queues). ਫ੍ਰੇਮਵਰਕ ਕਿਨਾਰੇ 'ਤੇ ਰਹਿੰਦਾ ਹੈ, ਕੇਂਦਰ ਵਿੱਚ ਨਹੀਂ।
ਜੇ ਤੁਸੀਂ ਮਾਈਕ੍ਰੋਸਰਵਿਸ-ਸਮਾਨ ਸਪਸ਼ਟਤਾ ਚਾਹੁੰਦੇ ਹੋ ਪਰ ਓਪਰੇਸ਼ਨਲ ਝੰਜਟ ਨਹੀਂ, ਤਾਂ modular monolith ਇੱਕ ਮਜ਼ਬੂਤ ਵਿਕਲਪ ਹੈ। ਤੁਸੀਂ ਇੱਕ ਹੀ deployable ਐਪ ਰੱਖਦੇ ਹੋ, ਪਰ ਇਸਨੂੰ feature modules (Billing, Accounts, Notifications) ਵਿੱਚ ਵੰਡਦੇ ਹੋ ਜਿਨ੍ਹਾਂ ਦੇ ਪਬਲਿਕ APIs ਹੋਂਦੇ ਹਨ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵорਕ ਇਸਨੂੰ ਆਸਾਨ ਬਣਾਉਂਦੇ ਹਨ ਕਿਉਂਕਿ ਓਟੋ-ਵਾਇਰ ਨਹੀਂ ਕਰਦੇ: ਹਰ module ਆਪਣੀਆਂ routes, dependencies, ਅਤੇ ਡੇਟਾ ਐਕਸੈਸ register ਕਰ ਸਕਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਹੱਦਬੰਦੀਆਂ ਦਿੱਸਦੀਆਂ ਹਨ ਅਤੇ ਅਚਾਨਕ ਲਾਂਘਣ ਮੁਸ਼ਕਲ ਹੁੰਦਾ ਹੈ।
ਇਨ੍ਹਾਂ ਤਿੰਨ ਪੈਟਰਨਾਂ ਵਿਚਕਾਰ ਲਾਭ ਇੱਕੋ ਹੈ: ਤੁਸੀਂ ਨਿਯਮ ਚੁਣਦੇ ਹੋ—ਫੋਲਡਰ ਲੇਆਊਟ, dependency direction, ਅਤੇ module ਸੀਮਾਵਾਂ—ਜਦਕਿ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਇਕ ਛੋਟੀ, ਸਥਿਰ ਸਤਹ ਦਿੰਦਾ ਹੈ ਜਿਸ ਵਿੱਚ ਤੁਸੀ plug-in ਕਰ ਸਕਦੇ ਹੋ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਨੂੰ ਛੋਟੇ ਤੋਂ ਸ਼ੁਰੂ ਕਰਨ ਅਤੇ ਲਚਕੀਲਾ ਰਹਿਣ ਦਿੰਦੇ ਹਨ, ਪਰ ਇਹ ਵੱਡਾ ਪ੍ਰਸ਼ਨ ਨਹੀਂ ਸੁਲਝਾਉਂਦੇ: ਤੁਹਾਡੀ ਸਿਸਟਮ ਦੀ "ਸ਼ਕਲ" ਕੀ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ? ਸਹੀ ਚੋਣ ਤਕਨੀਕ ਤੋਂ ਘੱਟ ਅਤੇ ਟੀਮ ਆਕਾਰ, ਰਿਲੀਜ਼ ਅਕਸ, ਅਤੇ coordination ਦੀ ਦਰਦ ਕਿੰਨੀ ਹੈ ਉਸ 'ਤੇ ਜ਼ਿਆਦਾ ਨਿਰਭਰ ਕਰਦੀ ਹੈ।
ਇੱਕ ਮੋਨੋਲਿਥ ਇੱਕ deployable ਯੂਨਿਟ ਵਜੋਂ ship ਹੁੰਦਾ ਹੈ। ਇਹ ਆਮ ਤੌਰ 'ਤੇ ਕੰਮ ਕਰਨ ਵਾਲਾ ਉਤਪਾਦ ਤਿਆਰ ਕਰਨ ਲਈ ਸਭ ਤੋਂ ਤੇਜ਼ ਰਾਹ ਹੁੰਦਾ ਹੈ: ਇਕ build, ਇਕ ਲੌਗ ਸੈੱਟ, ਇਕ ਥਾਂ ਡੀਬੱਗ।
ਇੱਕ ਮੋਡਿਊਲਰ ਮੋਨੋਲਿਥ ਹਾਲੇ ਵੀ ਇਕ deployable ਹੈ, ਪਰ ਅੰਦਰੂਨੀ ਤੌਰ 'ਤੇ ਸਪਸ਼ਟ modules ਵਿੱਚ ਵੰਡਿਆ ਗਿਆ ਹੈ (packages, bounded contexts, feature folders)। ਇਹ ਅਕਸਰ "ਅਗਲਾ ਕਦਮ" ਵਧੀਆ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਕੋਡਬੇਸ ਵਧਦਾ ਹੈ—ਖਾਸ ਕਰਕੇ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਨਾਲ, ਜਿੱਥੇ ਤੁਸੀਂ modules ਨੂੰ ਸਪਸ਼ਟ ਰੱਖ ਸਕਦੇ ਹੋ।
ਮਾਈਕ੍ਰੋਸਰਵਿਸਿਜ deployable ਨੂੰ ਕਈ ਸੇਵਾਵਾਂ ਵਿੱਚ ਵੰਡ ਦਿੰਦੇ ਹਨ। ਇਸ ਨਾਲ ਟੀਮਾਂ ਵਿਚਕਾਰ coupling ਘਟ ਸਕਦੀ ਹੈ, ਪਰ ਇਹ ਓਪਰੇਸ਼ਨਲ ਕੰਮ ਨੂੰ ਵੀ ਵਧਾ ਦਿੰਦਾ ਹੈ।
ਵੰਡੋ ਜਦੋਂ ਇੱਕ ਸੀਮਾ ਤੁਹਾਡੇ ਕੰਮ ਵਿੱਚ ਪਹਿਲਾਂ ਹੀ ਸਚਮੁਚ ਮੌਜੂਦ ਹੋ:
ਜਦ ਇਹ ਮੁਖ਼ਤਸਰ ਤਰ੍ਹਾਂ ਸੁਵਿਧਾ ਲਈ ਹੁੰਦਾ ਹੈ (“ਇਹ ਫੋਲਡਰ ਵੱਡਾ ਹੈ”) ਜਾਂ ਜੇ ਸੇਵਾਵਾਂ ਇੱਕੋ ਹੀ ਡੇਟਾਬੇਸ ਟੇਬਲਾਂ ਨੂੰ ਸ਼ੇਅਰ ਕਰਦੀਆਂ ਹਨ, ਤਾਂ ਵੰਡਣ ਤੋਂ ਬਚੋ। ਇਹ ਸੰਕੇਤ ਹੈ ਕਿ ਤੁਸੀਂ ਇੱਕ ਥਿਰ(boundary) ਨਹੀਂ ਲੱਭੇ।
ਇੱਕ API gateway clients ਨੂੰ ਸਧਾਰਨ ਕਰ ਸਕਦਾ ਹੈ (ਇਕ ਐਂਟਰੀ ਪੌਇੰਟ, ਕੇਂਦ੍ਰਿਤ auth/rate limiting). ਨੁਕਸਾਨ ਇਹ ਹੈ ਕਿ ਇਹ ਇੱਕ bottleneck ਜਾਂ single failure point ਬਣ ਸਕਦਾ ਹੈ ਜੇ ਇਹ ਬਹੁਤ ਵਧੇਰੇ ਜ਼ਿੰਮੇਵਾਰੀਆਂ ਲੈ ਲਏ।
Shared libraries ਵਿਕਾਸ ਨੂੰ ਤੇਜ਼ ਕਰਦੀਆਂ ਹਨ (ਆਮ validation, logging, SDKs), ਪਰ ਇਹ ਵੀ ਛੁਪੇ coupling पैदा ਕਰਦੀਆਂ ਹਨ। ਜੇ ਕਈ ਸੇਵਾਵਾਂ ਨੂੰ ਇੱਕਸਾਰ ਅੱਪਗਰੇਡ ਕਰਨਾ ਪੈਂਦਾ ਹੈ, ਤਾਂ ਤੁਸੀਂ distributed monolith ਹੀ ਬਣਾ ਲਿਆ।
ਮਾਈਕ੍ਰੋਸਰਵਿਸਿਜ ਦੁਹਰਾਏ ਜਾਣ ਵਾਲੇ ਖ਼ਰਚੇ ਵਧਾਉਂਦੇ ਹਨ: ਵੱਧ deploy pipelines, ਵਰਜਨਿੰਗ, service discovery, monitoring, tracing, incident response, ਅਤੇ on-call ਰੋਟੇਸ਼ਨ। ਜੇ ਤੁਹਾਡੀ ਟੀਮ ਇਹ ਮਸ਼ੀਨਰੀ ਆਰਾਮ ਨਾਲ ਚਲਾ ਨਹੀਂ ਸਕਦੀ, ਤਾਂ ਇੱਕ ਮੋਡਿਊਲਰ ਮੋਨੋਲਿਥ ਜੋ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਕੰਪੋਨੈਂਟਾਂ ਨਾਲ ਬਣਿਆ ਹੋਇਆ ਹੈ ਅਕਸਰ ਸੁਰੱਖਿਅਤ ਆਰਕੀਟੈਕਚਰ ਹੁੰਦਾ ਹੈ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਨੂੰ ਆਜ਼ਾਦੀ ਦਿੰਦਾ ਹੈ, ਪਰ ਰੱਖ-ਰਖਾਵ ਉਹ ਚੀਜ਼ ਹੈ ਜੋ ਤੁਹਾਨੂੰ ਡਿਜ਼ਾਈਨ ਕਰਨੀ ਪੈਂਦੀ ਹੈ। ਮਕਸਦ ਇਹ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ ਕਿ "ਕਸਟਮ" ਹਿੱਸੇ ਆਸਾਨੀ ਨਾਲ ਲੱਭੇ ਜਾ ਸਕਣ, ਬਦਲੇ ਜਾ ਸਕਣ, ਅਤੇ ਗ਼ਲਤ ਇਸਤੇਮਾਲ ਕਰਨਾ ਔਖਾ ਹੋਵੇ।
ਇੱਕ ਅਜਿਹੀ ਬਣਤਰ ਚੁਣੋ ਜੋ ਤੁਸੀਂ ਇੱਕ ਮਿੰਟ ਵਿੱਚ ਸਮਝਾ ਸਕੋ ਅਤੇ code review ਨਾਲ ਲਾਗੂ ਕਰੋ। ਇੱਕ ਕਾਰਗਰ ਵੰਡ ਇਹ ਹੋ ਸਕਦੀ ਹੈ:
app/ (composition root: modules ਨੂੰ ਵਾਇਰ ਕਰਦਾ ਹੈ)modules/ (ਬਿਜ਼ਨਸ ਸਮਰਥਾ)transport/ (HTTP routing, request/response mapping)shared/ (cross-cutting utilities: config, logging, error types)tests/ਨਾਮਕਰਨ consistent ਰੱਖੋ: module ਫੋਲਡਰਨਾਂ ਨਾਊਂਜ਼ ਵਰਤੇ ਜਾਣ ( billing, users), ਅਤੇ entry points predictable (index, routes, service).
ਹਰ module ਨੂੰ ਇੱਕ ਛੋਟੇ ਉਤਪਾਦ ਵਾਂਗ ਸਮਝੋ ਜਿਸ ਦੀਆਂ ਸਪਸ਼ਟ ਹੱਦਬੰਦੀਆਂ ਹੋਣ:
modules/users/public.ts)modules/users/internal/*)“reach-through” imports ਤੋਂ ਬਚੋ ਜਿਵੇਂ modules/orders/internal/db.ts ਨੂੰ ਕਿਸੇ ਹੋਰ module ਤੋਂ import ਕਰਨਾ। ਜੇ ਕਿਸੇ ਹੋਰ ਹਿੱਸੇ ਨੂੰ ਲੋੜ ਹੈ, ਤਾਂ ਉਸਨੂੰ public API 'ਚ ਉਠਾਓ।
ਛੋਟੇ ਸਰਵਿਸ ਨੂੰ ਵੀ ਬੁਨਿਆਦੀ visibility ਚਾਹੀਦੀ ਹੈ:
ਇਹਨਾਂ ਨੂੰ shared/observability ਵਿੱਚ ਰੱਖੋ ਤਾਂ ਜੋ ਹਰ route handler ਇਕੋ conventions ਵਰਤੇ।
ਗ੍ਰਾਹਕਾਂ ਲਈ errors predictable ਅਤੇ ਮਨੁੱਖ ਲਈ ਡੀਬੱਗ ਕਰਨ ਲਾਇਕ ਬਣਾਓ। ਇਕ error shape define ਕਰੋ (ਜਿਵੇਂ code, message, details, requestId) ਅਤੇ ਹਰ endpoint ਲਈ ਇੱਕ schema ਰੱਖੋ। ਅੰਦਰੂਨੀ exceptions ਨੂੰ HTTP responses ਵਿੱਚ ਨਕਸ਼ਾ ਕਰਨ ਲਈ mapping ਕੇਂਦ੍ਰਿਤ ਕਰੋ ਤਾ ਕਿ handlers ਬਿਜ਼ਨਸ ਲਾਜਿਕ 'ਤੇ ਕੇਂਦਰਤ ਰਹਿਣ।
ਜੇ ਤੁਹਾਡਾ ਮਕਸਦ ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਵਧਣਾ ਹੈ ਪਰ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ-ਸਟਾਈਲ ਆਰਕੀਟੈਕਚਰ ਸਪਸ਼ਟ ਰੱਖਣਾ ਹੈ, ਤਾਂ Koder.ai scaffolding ਅਤੇ iteration ਟੂਲ ਵਜੋਂ مفید ਹੋ ਸਕਦਾ ਹੈ ਨਾ ਕਿ ਚੰਗੀ ਡਿਜ਼ਾਈਨ ਦੀ ਜਗ੍ਹਾ। ਤੁਸੀਂ ਚੈਟ ਵਿੱਚ ਆਪਣੀਆਂ module boundaries, middleware stack, ਅਤੇ error format ਦੱਸ ਸਕਦੇ ਹੋ, ਇੱਕ ਕੰਮ ਕਰਨ ਵਾਲਾ baseline app (ਉਦਾਹਰਨ ਲਈ React frontend ਨਾਲ Go + PostgreSQL backend) ਜਨਰੇਟ ਕਰਾ ਸਕਦੇ ਹੋ, ਅਤੇ ਫਿਰ wiring ਨੂੰ ਸੁਚੇਤ ਤਰੀਕੇ ਨਾਲ ਸੋਧ ਸਕਦੇ ਹੋ।
ਦੋ ਖਾਸ ਫੀਚਰ ਖਾਸ ਕਰਕੇ ਕੰਮ ਆਉਂਦੇ ਹਨ:
ਕਿਉਂਕਿ Koder.ai source code export ਸਹਾਰਦਾ ਹੈ, ਤੁਸੀਂ ਕੋਡ ਦਾ ਮਾਲਕਾਨੂੰ ਰੱਖ ਸਕਦੇ ਹੋ ਅਤੇ ਆਪਣੀ repo ਵਿੱਚ ਇਸਨੂੰ ਹੱਥੋਂ-ਬਣੇ ਪ੍ਰਾਜੈੱਕਟ ਵਾਂਗੋਂ ਤਬਦੀਲ ਕਰ ਸਕਦੇ ਹੋ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ-ਆਧਾਰਿਤ ਸਿਸਟਮ "ਹੱਥ ਨਾਲ ਜੋੜੇ" ਮਹਿਸੂਸ ਹੋ ਸਕਦੇ ਹਨ, ਜਿਸ ਕਰਕੇ ਟੈਸਟਿੰਗ ਕਿਸੇ ਇੱਕ ਫਰੇਮਵਰਕ ਦੇ ਨਿਯਮਾਂ ਤੋਂ ਵੱਧ ਹਰ ਹਿੱਸੇ ਦੇ ਦਰਮਿਆਨ ਦੀ ਰਕਾਵਟਾਂ ਨੂੰ ਬਚਾਉਣ ਬਾਰੇ ਹੁੰਦੀ ਹੈ। ਮਕਸਦ ਯਕੀਨ ਹੈ ਬਿਨਾਂ ਇਹ ਕਿ ਹਰ ਬਦਲਾਅ ਨੂੰ ਪੂਰਾ end-to-end ਦੌੜ ਬਣਾਉਣਾ ਪਏ।
ਪਹਿਲਾਂ unit tests ਬਿਜ਼ਨਸ ਨਿਯਮਾਂ ਲਈ ਕਰੋ (validation, pricing, permissions logic) ਕਿਉਂਕਿ ਇਹ ਤੇਜ਼ ਹਨ ਅਤੇ ਫੇਲ੍ਹ੍ਹੀਆਂ ਨੂੰ ਨੁਕਤੀਆਨүүгөn ਦਿਖਾਉਂਦੀਆਂ ਹਨ।
ਫਿਰ ਕੁਝ ਉੱਚ-ਮੁੱਲ ਵਾਲੇ integration tests ਵਿੱਚ ਨਿਵੇਸ਼ ਕਰੋ ਜੋ wiring ਨੂੰ ਪਰਖਦੇ ਹਨ: routing → middleware → handler → persistence boundary। ਇਹ ਉਹ ਸਭ ਝੱਲੀਆਂ ਗਲਤੀਆਂ ਪਕੜਦੀਆਂ ਹਨ ਜਦੋਂ ਕੰਪੋਨੈਂਟ ਮਿਲਦੇ ਹਨ।
ਮਿਡਲਵੇਅਰ ਉਹ ਥਾਂ ਹੈ ਜਿੱਥੇ ਕ੍ਰਾਸ-ਕੱਟਿੰਗ ਵਰਤਾਰਾ ਛੁਪਿਆ ਹੁੰਦਾ ਹੈ (auth, logging, rate limits). ਇਸਨੂੰ pipeline ਵਾਂਗ ਟੈਸਟ ਕਰੋ:
Handlers ਲਈ, ਆਮ ਤੌਰ 'ਤੇ public HTTP ਆਕਾਰ (status codes, headers, response body) ਟੈਸਟ ਕਰੋ ਨਾ ਕਿ ਅੰਦਰੂਨੀ function calls। ਇਸ ਨਾਲ tests ਸਥਿਰ ਰਹਿੰਦੇ ਹਨ ਜਦੋਂ internals ਬਦਲਦੇ ਹਨ।
Dependency injection (ਜਾਂ simple constructor parameters) ਨੂੰ ਵਰਤ ਕੇ ਅਸਲੀ dependencies ਦੀ ਥਾਂ fakes ਰੱਖੋ:
ਜਦੋਂ ਕਈ ਸੇਵਾਵਾਂ ਜਾਂ ਟੀਮਾਂ ਕਿਸੇ API 'ਤੇ ਨਿਰਭਰ ਹੋਣ, contract tests ਜੋੜੋ ਜੋ request/response ਉਮੀਦਾਂ ਨੂੰ ਨੇਮਬੱਧ ਕਰ ਦਿੰਦੀਆਂ ਹਨ। provider-side contract tests ਯਕੀਨੀ ਬਣਾਉਂਦੀਆਂ ਹਨ ਕਿ ਤੁਸੀਂ consumers ਨੂੰ ਅਕਸਮਾਤ ਤੌਰ 'ਤੇ ਤੋੜੋ ਨਹੀਂ, ਭਾਵੇਂ ਕਿ ਤੁਹਾਡੀ ਮਾਈਕ੍ਰोਫਰੇਮਵਰਕ ਸੈਟਅਪ ਅਤੇ ਅੰਦਰੂਨੀ modules ਵਿਕਸਤ ਹੋ ਰਹੇ ਹੋਣ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਨੂੰ ਆਜ਼ਾਦੀ ਦਿੰਦੀਆਂ ਹਨ, ਪਰ ਆਜ਼ਾਦੀ ਖੁਦ-ਬ-ਖੁਦ ਸਪਸ਼ਟਤਾ ਨਹੀਂ ਲਿਆਉਂਦੀ। ਮੁੱਖ ਜੋਖਮ ਬਾਅਦ ਵਿੱਚ ਆਉਂਦੇ ਹਨ—ਜਦੋਂ ਟੀਮ ਵਧਦੀ ਹੈ, ਕੋਡਬੇਸ ਵੱਡਾ ਹੁੰਦਾ ਹੈ, ਅਤੇ "ਅਸਥਾਈ" ਫੈਸਲੇ ਸਥਾਈ ਬਣ ਜਾਂਦੇ ਹਨ।
ਘੱਟ ਬਿਲਟ-ਇਨ conventions ਹੋਣ ਨਾਲ, ਦੋ ਟੀਮ ਇੱਕੋ ਫੀਚਰ ਨੂੰ ਦੋ ਵੱਖਰੇ ਢੰਗ ਨਾਲ ਬਣਾਉਣਗੀਆਂ (ਰਾਊਟਿੰਗ, error handling, response formats, logging)। ਇਹ inconsistent ਹੋਣਾ review धीਮਾ ਕਰਦਾ ਅਤੇ onboarding ਮੁਸ਼ਕਲ ਬਣਾਉਂਦਾ ਹੈ।
ਇੱਕ ਸਰਲ guardrail ਮਦਦ ਕਰਦਾ ਹੈ: ਇਕ ਛੋਟੀ "service template" doc লিখੋ (project structure, naming, error format, logging fields) ਅਤੇ ਇਸਨੂੰ starter repo ਅਤੇ ਕੁਝ lints ਨਾਲ enforce ਕਰੋ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਪ੍ਰਾਜੈਕਟ ਆਮ ਤੌਰ 'ਤੇ ਸਾਫ਼ ਸ਼ੁਰੂ ਹੁੰਦੇ ਹਨ, ਫਿਰ ਇਕ utils/ ਫੋਲਡਰ ਇਕ ਥਰਡ-ਫਰੇਮਵਰਕ ਬਣ ਕੇ ਵਿਕਸਿਤ ਹੋ ਜਾਂਦਾ ਹੈ। ਜਦੋਂ modules helpers, constants, ਅਤੇ global state ਸਾਂਝੇ ਕਰਦੇ ਹਨ, ਸੀਮਾਵਾਂ ਧੁੰਦਲ ਹੋ ਜਾਂਦੀਆਂ ਹਨ ਅਤੇ ਬਦਲਾਅ ਅਚਨਚੇਤ ਤੌਰ 'ਤੇ ਟੁਟਫਟਿ ਪੈਦਾ ਕਰਦੇ ਹਨ।
ਸਪਸ਼ਟ shared packages ਵਰਤੋ ਜਿਨ੍ਹਾਂ ਵਿੱਚ versioning ਹੋਵੇ, ਜਾਂ sharing ਘੱਟ ਰੱਖੋ: types, interfaces, ਅਤੇ ਚੰਗੀ ਤਰ੍ਹਾਂ ਟੈਸਟ ਕੀਤੇ primitive methods। ਜੇ ਕੋਈ helper business rules 'ਤੇ ਨਿਰਭਰ ਹੈ, ਤਾਂ ਉਹ ਸੰਭਵਤ: domain module ਵਿੱਚ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ, ਨਾ ਕਿ "utils" ਵਿੱਚ।
ਜਦੋਂ ਤੁਸੀਂ authentication, authorization, input validation, ਅਤੇ rate limiting ਹੱਥੋਂ-ਹੱਥ wire ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਰੂਟ ਛੱਡ ਜਾਣ, middleware ਭੁੱਲ ਜਾਣ, ਜਾਂ ਸਿਰਫ "happy path" inputs validate ਕਰਨ ਵਾਂਗੀਆਂ ਗਲਤੀਆਂ ਹੋ ਸਕਦੀਆਂ ਹਨ।
ਸੁਰੱਖਿਆ defaults ਕੇਂਦ੍ਰਿਤ ਕਰੋ: secure headers, consistent auth checks, ਅਤੇ ਇਨਪੁਟ validation ਏਜ 'ਤੇ। Protected endpoints ਦੀ ਜਾਂਚ ਕਰਨ ਵਾਲੇ tests ਸ਼ਾਮِل ਕਰੋ।
ਬੇ-ਯੋਜਨਾ ਮਿਡਲਵੇਅਰ ਲੇਅਰਿੰਗ overhead ਵਧਾ ਸਕਦੀ ਹੈ—ਖ਼ਾਸ ਕਰਕੇ ਜੇ ਕਈ middleware bodies parse ਕਰਦੇ ਹਨ, storage 'ਤੇ ਹਿਟ ਕਰਦੇ ਹਨ, ਜਾਂ logs serialize ਕਰਦੇ ਹਨ।
Middleware ਛੋਟੇ ਅਤੇ ਮਾਪਯੋਗ ਰੱਖੋ। standard order ਨੂੰ ਦਸਤਾਵੇਜ਼ ਕਰੋ, ਅਤੇ ਨਵੇਂ middleware ਲਈ ਲਾਗਤ ਦੀ ਸਮੀਖਿਆ ਕਰੋ। ਜੇ ਤੁਸੀਂ ਬਲੋਟ ਦਾ ਸਸ਼ਕਸ ਹੋ, ਤਾਂ request profile ਕਰੋ ਅਤੇ redundant steps ਹਟਾਓ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਨੂੰ ਚੋਣਾਂ ਦਿੰਦੇ ਹਨ—ਪਰ ਚੋਣਾਂ ਨੂੰ ਇੱਕ ਪ੍ਰਕਿਰਿਆ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਮਕਸਦ ਇਹ ਨਹੀਂ ਕਿ "ਸਭ ਤੋਂ ਵਧੀਆ" ਆਰਕੀਟੈਕਚਰ ਲੱਭੋ; ਮਕਸਦ ਇਹ ਹੈ ਕਿ ਇੱਕ ਐਸੀ ਸ਼ਕਲ ਚੁਣੋ ਜਿਸਨੂੰ ਤੁਹਾਡੀ ਟੀਮ ਬਿਨਾਂ ਜ਼ਿਆਦਾ ਸੰਕਟ ਦੇ ਬਣਾਉਣ, ਚਲਾਉਣ, ਅਤੇ ਬਦਲਣ ਯੋਗ ਹੋਵੇ।
"ਮੋਨੋਲਿਥ" ਜਾਂ "ਮਾਈਕ੍ਰੋਸਰਵਿਸ" ਚੁਣਨ ਤੋਂ ਪਹਿਲਾਂ ਇਨ੍ਹਾਂ ਦਾ ਜਵਾਬ ਦਿਓ:
ਜੇ ਅਨਿਸ਼ਚਿਤ ਹੋ, ਤਾਂ ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਨਾਲ ਬਣਿਆ modular monolith ਨੂੰ ਡੀਫਾਲਟ ਕਰੋ। ਇਹ ਸੀਮਾਵਾਂ ਸਪਸ਼ਟ ਰੱਖਦਾ ਹੈ ਅਤੇ ship ਕਰਨਾ ਆਸਾਨ ਰੱਖਦਾ ਹੈ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਤੁਹਾਡੇ ਲਈ consistency enforce ਨਹੀਂ ਕਰਦੇ, ਇਸ ਲਈ conventions ਪਹਿਲਾਂ ਤੋਂ ਚੁਣੋ:
ਇੱਕ ਇਕ-ਪੰਨਾ "service contract" ਦਸਤਾਵੇਜ਼ /docs ਵਿੱਚ ਰੱਖੋ।
ਉਹ cross-cutting ਹਿੱਸੇ ਸ਼ੁਰੂ ਵਿੱਚ ਜੋ ਹਰ ਜਗ੍ਹਾ ਲੋੜੀਂਦੇ ਹਨ:
ਇਨ੍ਹਾਂ ਨੂੰ shared modules ਵਜੋਂ ਰੱਖੋ, ਨਾ ਕਿ copy-paste snippets ਵਜੋਂ।
ਆਰਕੀਟੈਕਚਰ ਨੂੰ ਜ਼ਰੂਰਤਾਂ ਦੇ ਅਨੁਸਾਰ ਬਦਲਣਾ ਚਾਹੀਦਾ ਹੈ। ਹਰ ਤਿਮਾਹੀ, ਰਿਵਿਊ ਕਰੋ ਕੀ deploys ਸੌਖੇ ਹਨ, ਕਿਹੜੇ ਹਿੱਸੇ ਵੱਖ-ਵੱਖ ਤਰੀਕੇ ਨਾਲ scale ਹੋ ਰਹੇ ਹਨ, ਅਤੇ ਕੀ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਟੁੱਟਦਾ ਹੈ। ਜੇ ਇਕ ਡੋਮੇਨ ਘਟਨਾ ਬਣ ਕੇ ਰਿਹਾ ਹੈ, ਉਹ ਤੁਹਾਡਾ ਅਗਲਾ ਉਮੀਦਵਾਰ ਹੋਵੇ—ਸੋ ਕWhole system ਨੂੰ ਨਹੀਂ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਸੈਟਅਪ ਆਮ ਤੌਰ 'ਤੇ "ਪੂਰੀ ਤਰ੍ਹਾਂ ਡਿਜ਼ਾਇਨ ਕੀਤੀ" ਸ਼ੁਰੂ ਨਹੀਂ ਹੁੰਦੀ। ਇਹ ਆਮ ਤੌਰ 'ਤੇ ਇੱਕ API, ਇਕ ਟੀਮ, ਅਤੇ ਇੱਕ ਟਾਈਟ ਡੇਡਲਾਈਨ ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ। ਮੁੱਲ ਉਸ समय ਆਉਂਦਾ ਹੈ ਜਦੋਂ ਉਤਪਾਦ ਵਧਦਾ ਹੈ: ਨਵੇਂ ਫੀਚਰ ਆਉਂਦੇ ਹਨ, ਹੋਰ ਲੋਕ ਕੋਡ ਨੂੰ ਛੁਹਦੇ ਹਨ, ਅਤੇ ਤੁਹਾਡੀ ਆਰਕੀਟੈਕਚਰ ਨੂੰ ਤਣਾਅ ਨੂੰ ਸਹਿਣਾ ਪੈਂਦਾ ਹੈ।
ਤੁਸੀਂ ਇਕ ਘੱਟੋ-ਘੱਟ ਸੇਵਾ ਨਾਲ ਸ਼ੁਰੂ ਕਰਦੇ ਹੋ: routing, request parsing, ਅਤੇ ਇੱਕ ਡੇਟਾਬੇਸ ਐਡਾਪਟਰ। ਜ਼ਿਆਦਾਤਰ ਲਾਜਿਕ ਤੇਜ਼ੀ ਨਾਲ ship ਕਰਨ ਲਈ endpoints ਨੇੜੇ ਰਹਿੰਦੀ ਹੈ।
ਜਿਵੇਂ ਹੀ ਤੁਸੀਂ auth, payments, notifications, ਅਤੇ reporting ਜੋੜਦੇ ਹੋ, ਤੁਹਾਨੂੰ ਉਹਨਾਂ ਨੂੰ modules (folders ਜਾਂ packages) ਵਿੱਚ ਵੰਡਣੀ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ। ਹਰ module ਆਪਣੇ models, business rules, ਅਤੇ data access ਦਾ ਮਾਲਕ ਹੋਂਦਾ ਹੈ, ਅਤੇ ਕੇਵਲ ਉਹੀ expose ਕਰਦਾ ਜੋ ਹੋਰ modules ਨੂੰ ਚਾਹੀਦਾ ਹੈ।
Logging, auth checks, rate limiting, ਅਤੇ request validation middleware ਵਿੱਚ ਆ ਜਾਂਦੇ ਹਨ ਤਾਂ ਜੋ ਹਰ endpoint ਦਾ ਵਿਹਾਰ ਇੱਕਸਾਰ ਰਹੇ। ਕਿਉਂਕਿ ਕ੍ਰਮ ਲਈ ਮਹੱਤਤਾ ਹੈ, ਇਸਨੂੰ ਦਸਤਾਵੇਜ਼ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ।
ਦਸਤਾਵੇਜ਼ ਕਰੋ:
ਜਦੋਂ modules ਬਹੁਤ ਸਾਰੀ ਅੰਦਰੂਨੀ ਸਾਂਝੇਦਾਰੀ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰ ਦੇਂਦੇ ਹਨ, build times ਖਰਾਬ ਹੋਣ, ਜਾਂ "ਛੋਟੀ ਬਦਲਾਅ" ਲਈ ਕਈ modules ਵਿੱਚ ਸੋਧ ਕਰਨੀਆਂ ਪੈਂਦੀਆਂ ਹਨ, ਤਾਂ refactor ਕਰੋ।
ਜਦੋਂ ਟੀਮਾਂ shared deployments ਨਾਲ ਰੁਕ ਜਾਂਦੀਆਂ ਹਨ, ਹਿੱਸੇ ਵੱਖ-ਵੱਖ ਤਰੀਕੇ ਨਾਲ scale ਕਰਨ, ਜਾਂ ਇਕ ਇੰਟੀਗ੍ਰੇਸ਼ਨ ਬਾਊਂਡਰੀ ਅਲਰੇਡੀ ਅਲੱਗ ਉਤਪਾਦ ਵਾਂਗ ਵਰਤ ਰਹੀ ਹੋਵੇ, ਤਾਂ ਸੇਵਾਵਾਂ ਵੰਡਣ 'ਤੇ ਵਿਚਾਰ ਕਰੋ।
ਮਾਈਕ੍ਰੋਫਰੇਮਵਰਕ ਉਹਨਾਂ ਲਈ ਵਧੀਆ ਹਨ ਜੋ ਚਾਹੁੰਦੇ ਹਨ ਕਿ ਐਪਲੀਕੇਸ਼ਨ ਉਹਨਾਂ ਦੇ ਡੋਮੇਨ ਦੇ ਆਲੇ-ਦੁਆਲੇ ਬਣੇ ਨਾ ਕਿ ਇਕ ਨਿਰਧਾਰਿਤ ਸਟੈਕ ਦੇ ਆਲੇ-ਦੁਆਲੇ। ਇਹ ਖ਼ਾਸ ਕਰਕੇ ਉਹਨਾਂ ਟੀਮਾਂ ਲਈ ਚੰਗੇ ਹਨ ਜਿਨ੍ਹਾਂ ਨੂੰ ਸੁਨਿਛਿਤਤਾ ਪਸੰਦ ਹੈ: ਤੁਸੀਂ ਕੁਝ ਮੁੱਖ ਬਿਲਡਿੰਗ ਬਲਾਕ ਚੁਣਦੇ ਹੋ ਅਤੇ ਉਸ ਦੇ ਬਦਲੇ ਤੁਹਾਨੂੰ ਇੱਕ ਸਮਝਣਯੋਗ ਕੋਡਬੇਸ ਮਿਲਦਾ ਹੈ ਜਦੋਂ ਲੋੜ ਬਦਲਦੀ ਹੈ।
ਤੁਹਾਡੀ ਲਚਕੀਲਾਪਣ ਦਾ ਫਾਇਦਾ ਸਿਰਫ਼ ਉਹਨਾਂ ਆਦਤਾਂ ਨਾਲ ਹੀ ਮਿਲਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਦੀ ਰੱਖਿਆ ਕੀਤੀ ਜਾਏ:
ਦੋ ਹਲਕੇ artifacts ਨਾਲ ਸ਼ੁਰੂ ਕਰੋ:
ਅੰਤ ਵਿੱਚ, ਜਦੋਂ ਤੁਸੀਂ ਫੈਸਲੇ ਕਰੋ, ਉਹਨਾਂ ਨੂੰ ਦਸਤਾਵੇਜ਼ ਕਰੋ—ਛੋਟੀ ਨੋਟਸ ਵੀ ਮਦਦਗਾਰ ਹੁੰਦੀਆਂ ਹਨ। ਆਪਣੀ repo ਵਿੱਚ "Architecture Decisions" ਪੇਜ ਬਣਾ ਕੇ ਸਮਿਆਂ-ਸਮੇਂ 'ਤੇ ਰਿਵਿਊ ਕਰੋ ਤਾਂ ਕਿ ਕੱਲ੍ਹ ਦੇ ਸਕਾਰਟਕਾਂ ਹਨ ਕਿ ਅੱਜ ਦੇ ਛੋਟੇ-ਛੋਟੇ shortcuts ਕੱਲ੍ਹ ਦੀਆਂ ਪਾਬੰਦੀਆਂ ਨਾ ਬਣ ਜਾਣ।
A microframework focuses on the essentials: routing, request/response handling, and basic extension points.
A full-stack framework typically bundles many “batteries included” features (ORM, auth, admin, forms, background jobs). Microframeworks trade convenience for control—you add only what you need and decide how pieces connect.
Microframeworks are a good fit when you want to:
A “smallest useful core” is usually:
Start there, ship one endpoint, then add modules only when they clearly pay for themselves (auth, validation, observability, queues).
Middleware is best for cross-cutting concerns that apply broadly, such as:
Keep route handlers focused on business logic: parse → call service → return response.
Order changes behavior. A common, reliable sequence is:
Document the order near setup code so future changes don’t silently break responses or security assumptions.
Inversion of Control means your business code doesn’t construct its own dependencies (it doesn’t “go shopping”). Instead, the application wiring provides what it needs.
Practically: build the database client, logger, and API clients at startup, then pass them into services/handlers. This reduces tight coupling and makes testing and swapping implementations much easier.
No. You can get most DI benefits with a simple composition root:
Add a container only if the dependency graph becomes painful to manage manually—don’t start with complexity by default.
Put storage and external APIs behind small interfaces (ports), then implement adapters:
UserRepository interface with findById, create, listPostgresUserRepository for productionA practical structure that keeps boundaries visible:
app/ composition root (wiring)modules/ feature modules (domain capabilities)transport/ HTTP routing + request/response mappingshared/ config, logging, error types, observabilityPrioritize fast unit tests for business rules, then add fewer high-value integration tests that exercise the full pipeline (routing → middleware → handler → persistence boundary).
Use DI/fakes to isolate external services, and test middleware like a pipeline (assert headers, side effects, and blocking behavior). If multiple teams depend on APIs, add contract tests to prevent breaking changes.
InMemoryUserRepository for testsHandlers/services depend on the interface, not the concrete tool. Switching databases or third-party providers becomes a wiring/config change, not a rewrite.
tests/Enforce module public APIs (e.g., modules/users/public.ts) and avoid “reach-through” imports into internals.