JavaScript માં ડોમેન-ડ્રિવન ડિઝાઇન માસ્ટર કરો. મજબૂત ડોમેન ઓબ્જેક્ટ મોડેલ્સ સાથે સ્કેલેબલ, પરીક્ષણક્ષમ અને જાળવણીક્ષમ એપ્લિકેશનો બનાવો.
JavaScript Module Entity Patterns: ડોમેન ઓબ્જેક્ટ મોડેલિંગમાં ઊંડાણપૂર્વક
સોફ્ટવેર ડેવલપમેન્ટની દુનિયામાં, ખાસ કરીને JavaScript ઇકોસિસ્ટમના ગતિશીલ અને સતત વિકસતા વાતાવરણમાં, આપણે ઘણીવાર ગતિ, ફ્રેમવર્ક અને સુવિધાઓને પ્રાધાન્ય આપીએ છીએ. અમે જટિલ વપરાશકર્તા ઇન્ટરફેસ બનાવીએ છીએ, અસંખ્ય API સાથે કનેક્ટ થઈએ છીએ, અને ઝડપથી એપ્લિકેશનો ડિપ્લોય કરીએ છીએ. પરંતુ આ દોડધામમાં, આપણે ઘણીવાર આપણી એપ્લિકેશનના મૂળને અવગણીએ છીએ: બિઝનેસ ડોમેન. આ 'બિગ બોલ ઓફ મડ' તરફ દોરી શકે છે—એક સિસ્ટમ જ્યાં બિઝનેસ લોજિક વિખરાયેલું હોય, ડેટા અવ્યવસ્થિત હોય, અને એક સરળ ફેરફાર અણધાર્યા બગ્સનું કારણ બની શકે.
આ તે છે જ્યાં ડોમેન ઓબ્જેક્ટ મોડેલિંગ આવે છે. તે તમે જે સમસ્યા ક્ષેત્રમાં કામ કરી રહ્યા છો તેનું સમૃદ્ધ, અભિવ્યક્ત મોડેલ બનાવવાની પ્રથા છે. અને JavaScript માં, Module Entity Pattern આ પ્રાપ્ત કરવાની એક શક્તિશાળી, ભવ્ય અને ફ્રેમવર્ક-અજ્ઞેયવાદી રીત છે. આ વ્યાપક માર્ગદર્શિકા તમને આ પેટર્નના સિદ્ધાંત, વ્યવહાર અને લાભો વિશે માર્ગદર્શન આપશે, તમને વધુ મજબૂત, સ્કેલેબલ અને જાળવણીક્ષમ એપ્લિકેશનો બનાવવાની શક્તિ આપશે.
ડોમેન ઓબ્જેક્ટ મોડેલિંગ શું છે?
પેટર્નમાં ડાઇવ કરતા પહેલા, ચાલો આપણા શબ્દો સ્પષ્ટ કરીએ. બ્રાઉઝરના ડોક્યુમેન્ટ ઓબ્જેક્ટ મોડેલ (DOM) થી આ ખ્યાલને અલગ પાડવો મહત્વપૂર્ણ છે.
- ડોમેન: સોફ્ટવેરમાં, 'ડોમેન' એ વિશિષ્ટ વિષય ક્ષેત્ર છે જે વપરાશકર્તાના વ્યવસાય સાથે સંબંધિત છે. ઈ-કોમર્સ એપ્લિકેશન માટે, ડોમેનમાં Products, Customers, Orders, અને Payments જેવા ખ્યાલોનો સમાવેશ થાય છે. સોશિયલ મીડિયા પ્લેટફોર્મ માટે, તેમાં Users, Posts, Comments, અને Likes નો સમાવેશ થાય છે.
- ડોમેન ઓબ્જેક્ટ મોડેલિંગ: આ તે બિઝનેસ ડોમેનની અંદર એન્ટિટીઝ, તેમના વર્તણૂક અને તેમના સંબંધોનું પ્રતિનિધિત્વ કરતું સોફ્ટવેર મોડેલ બનાવવાની પ્રક્રિયા છે. તે વાસ્તવિક દુનિયાના ખ્યાલોને કોડમાં અનુવાદિત કરવા વિશે છે.
એક સારું ડોમેન મોડેલ ફક્ત ડેટા કન્ટેનરનો સંગ્રહ નથી. તે તમારા વ્યવસાય નિયમોનું જીવંત પ્રતિનિધિત્વ છે. એક Order ઓબ્જેક્ટમાં ફક્ત વસ્તુઓની સૂચિ ન હોવી જોઈએ; તેને તેના કુલની ગણતરી કરવી જોઈએ, નવી વસ્તુ કેવી રીતે ઉમેરવી જોઈએ, અને શું તેને રદ કરી શકાય છે તે જાણવું જોઈએ. ડેટા અને વર્તણૂકનું આ એન્કેપ્સ્યુલેશન એ એક સ્થિતિસ્થાપક એપ્લિકેશન કોર બનાવવાની ચાવી છે.
સામાન્ય સમસ્યા: "મોડેલ" લેયરમાં અરાજકતા
ઘણી JavaScript એપ્લિકેશનોમાં, ખાસ કરીને જે કાર્બનિક રીતે વિકસિત થાય છે, 'મોડેલ' લેયર ઘણીવાર પછીથી વિચારવામાં આવે છે. આપણે વારંવાર આ એન્ટિ-પેટર્ન જોઈએ છીએ:
// Somewhere in an API controller or service...
async function createUser(req, res) {
const { email, password, firstName, lastName } = req.body;
// Business logic and validation is scattered here
if (!email || !email.includes('@')) {
return res.status(400).send({ error: 'A valid email is required.' });
}
if (!password || password.length < 8) {
return res.status(400).send({ error: 'Password must be at least 8 characters.' });
}
const user = {
email: email.toLowerCase(),
password: await hashPassword(password), // Some utility function
fullName: `${firstName} ${lastName}`, // Logic for derived data is here
createdAt: new Date()
};
// Now, what is `user`? It's just a plain object.
// Nothing stops another developer from doing this later:
// user.email = 'an-invalid-email';
// user.password = 'short';
await db.users.insert(user);
res.status(201).send(user);
}
આ અભિગમ અનેક ગંભીર સમસ્યાઓ રજૂ કરે છે:
- સત્યનો એકમાત્ર સ્ત્રોત નથી: 'વપરાશકર્તા' માન્ય શું છે તેના નિયમો આ એક કંટ્રોલરમાં વ્યાખ્યાયિત થયેલા છે. જો સિસ્ટમના અન્ય ભાગને વપરાશકર્તા બનાવવાની જરૂર હોય તો શું? શું તમે લોજિક કોપી-પેસ્ટ કરો છો? આ અસંગતતા અને બગ્સ તરફ દોરી જાય છે.
- એનિમિક ડોમેન મોડેલ: `user` ઓબ્જેક્ટ ફક્ત 'મૂંગો' ડેટા બેગ છે. તેમાં કોઈ વર્તણૂક અને કોઈ આત્મ-જાગૃતિ નથી. તેના પર કાર્ય કરતી બધી લોજિક બહાર રહે છે.
- ઓછો સહયોગ: વપરાશકર્તાના સંપૂર્ણ નામ બનાવવાની લોજિક API વિનંતી/પ્રતિભાવ હેન્ડલિંગ અને પાસવર્ડ હેશિંગ સાથે ભળી જાય છે.
- પરીક્ષણ કરવું મુશ્કેલ: વપરાશકર્તા બનાવવાની લોજિકનું પરીક્ષણ કરવા માટે, તમારે HTTP વિનંતીઓ અને પ્રતિભાવો, ડેટાબેસેસ અને હેશિંગ કાર્યોને મોક કરવું પડશે. તમે ફક્ત 'વપરાશકર્તા' ખ્યાલનું અલગથી પરીક્ષણ કરી શકતા નથી.
- ગર્ભિત કરારો: બાકીની એપ્લિકેશનને ફક્ત 'ધારવું' પડશે કે વપરાશકર્તાનું પ્રતિનિધિત્વ કરતો કોઈપણ ઓબ્જેક્ટ ચોક્કસ આકાર ધરાવે છે અને તેનો ડેટા માન્ય છે. કોઈ ગેરંટી નથી.
ઉકેલ: JavaScript Module Entity Pattern
Module Entity Pattern આ સમસ્યાઓનું નિરાકરણ એક જ ડોમેન ખ્યાલ વિશે બધું જ વ્યાખ્યાયિત કરવા માટે પ્રમાણભૂત JavaScript મોડ્યુલ (એક ફાઇલ) નો ઉપયોગ કરીને કરે છે. આ મોડ્યુલ તે એન્ટિટી માટે સત્યનો નિર્ણાયક સ્ત્રોત બની જાય છે.
Module Entity સામાન્ય રીતે factory function બહાર પાડે છે. આ કાર્ય એન્ટિટીની માન્ય નકલ બનાવવાની જવાબદારી ધરાવે છે. તે જે object પરત કરે છે તે ફક્ત ડેટા નથી; તે એક સમૃદ્ધ ડોમેન ઓબ્જેક્ટ છે જે તેના પોતાના ડેટા, માન્યતા અને બિઝનેસ લોજિકને એન્કેપ્સ્યુલેટ કરે છે.
Module Entity ની મુખ્ય લાક્ષણિકતાઓ
- એન્કેપ્સ્યુલેશન: તે ડેટા અને તે ડેટા પર કાર્ય કરતા કાર્યોને એકસાથે બંડલ કરે છે.
- સીમા પર માન્યતા: તે ખાતરી કરે છે કે અમાન્ય એન્ટિટી બનાવવી અશક્ય છે. તે તેની પોતાની સ્થિતિનું રક્ષણ કરે છે.
- સ્પષ્ટ API: તે એન્ટિટી સાથે સંપર્ક કરવા માટે કાર્યોનો સ્વચ્છ, ઇરાદાપૂર્વકનો સમૂહ (એક જાહેર API) બહાર પાડે છે, જ્યારે આંતરિક અમલીકરણ વિગતો છુપાવે છે.
- અપરિવર્તનશીલતા: તે આકસ્મિક સ્થિતિ ફેરફારોને રોકવા અને અનુમાનિત વર્તણૂક સુનિશ્ચિત કરવા માટે ઘણીવાર અપરિવર્તનશીલ અથવા ફક્ત-વાંચવા યોગ્ય object ઉત્પન્ન કરે છે.
- પોર્ટેબિલિટી: તેમાં ફ્રેમવર્ક (જેમ કે Express, React) અથવા બાહ્ય સિસ્ટમો (જેમ કે ડેટાબેસેસ, API) પરથી શૂન્ય નિર્ભરતા છે. તે શુદ્ધ બિઝનેસ લોજિક છે.
Module Entity ના મુખ્ય ઘટકો
ચાલો આ પેટર્નનો ઉપયોગ કરીને અમારા `User` ખ્યાલનું પુનર્નિર્માણ કરીએ. અમે એક ફાઇલ, `user.js` (અથવા TypeScript વપરાશકર્તાઓ માટે `user.ts`) બનાવીશું, અને તેને સ્ટેપ-બાય-સ્ટેપ બનાવીશું.
1. Factory Function: તમારું Object Constructor
ક્લાસને બદલે, અમે factory function (દા.ત., `buildUser`) નો ઉપયોગ કરીશું. ફેક્ટરીઓ મહાન સુગમતા પ્રદાન કરે છે, `this` કીવર્ડ સાથે કુસ્તી ટાળે છે, અને JavaScript માં ખાનગી સ્થિતિ અને એન્કેપ્સ્યુલેશનને વધુ કુદરતી બનાવે છે.
અમારો ધ્યેય એક કાર્ય બનાવવાનો છે જે કાચા ડેટા લે છે અને સારી રીતે રચાયેલ, વિશ્વસનીય User ઓબ્જેક્ટ પરત કરે છે.
// file: /domain/user.js
export default function buildMakeUser() {
// This inner function is the actual factory.
// It has access to any dependencies passed to buildMakeUser, if needed.
return function makeUser({
id = generateId(), // Let's assume a function to generate a unique ID
firstName,
lastName,
email,
passwordHash,
createdAt = new Date()
}) {
// ... validation and logic will go here ...
const user = {
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => email,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt
};
// Using Object.freeze to make the object immutable.
return Object.freeze(user);
}
}
અહીં કેટલીક બાબતોની નોંધ લો. અમે એક કાર્યનો ઉપયોગ કરી રહ્યા છીએ જે એક કાર્ય પરત કરે છે (એક ઉચ્ચ-ક્રમાંકિત કાર્ય). આ નિર્ભરતાઓને ઇન્જેક્ટ કરવા માટે એક શક્તિશાળી પેટર્ન છે, જેમ કે અનન્ય ID જનરેટર અથવા માન્યકર્તા લાઇબ્રેરી, એન્ટિટીને ચોક્કસ અમલીકરણ સાથે જોડ્યા વિના. અત્યાર માટે, અમે તેને સરળ રાખીશું.
2. Data Validation: ગેટ પર રક્ષક
એક એન્ટિટીએ તેની પોતાની અખંડિતતાનું રક્ષણ કરવું આવશ્યક છે. અમાન્ય સ્થિતિમાં `User` બનાવવું અશક્ય હોવું જોઈએ. અમે તેને ફેક્ટરી ફંક્શનની અંદર જ માન્યતા ઉમેરીએ છીએ. જો ડેટા અમાન્ય હોય, તો ફેક્ટરીએ ભૂલ ફેંકવી જોઈએ, જે સ્પષ્ટપણે જણાવે છે કે શું ખોટું છે.
// file: /domain/user.js
export default function buildMakeUser({ Id, isValidEmail, hashPassword }) {
return function makeUser({
id = Id.makeId(),
firstName,
lastName,
email,
password, // We now take a plain password and handle it inside
createdAt = new Date()
}) {
if (!Id.isValidId(id)) {
throw new Error('User must have a valid id.');
}
if (!firstName || firstName.length < 2) {
throw new Error('First name must be at least 2 characters long.');
}
if (!lastName || lastName.length < 2) {
throw new Error('Last name must be at least 2 characters long.');
}
if (!email || !isValidEmail(email)) {
throw new Error('User must have a valid email address.');
}
if (!password || password.length < 8) {
throw new Error('Password must be at least 8 characters long.');
}
// Data normalization and transformation happens here
const passwordHash = hashPassword(password);
const normalizedEmail = email.toLowerCase();
return Object.freeze({
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => normalizedEmail,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt
});
}
}
હવે, અમારી સિસ્ટમનો કોઈપણ ભાગ જે `User` બનાવવા માંગે છે તેણે આ ફેક્ટરીમાંથી પસાર થવું આવશ્યક છે. અમને દરેક વખતે ગેરંટીકૃત માન્યતા મળે છે. અમે પાસવર્ડ હેશિંગ અને ઈમેલ નોર્મલાઇઝેશનના લોજિકને પણ એન્કેપ્સ્યુલેટ કર્યું છે. બાકીની એપ્લિકેશનને આ વિગતો જાણવાની કે કાળજી લેવાની જરૂર નથી.
3. Business Logic: વર્તણૂકનું એન્કેપ્સ્યુલેશન
અમારો `User` ઓબ્જેક્ટ હજુ થોડો એનિમિક છે. તે ડેટા ધરાવે છે, પરંતુ તે કંઈ *કરતું* નથી. ચાલો વર્તણૂક ઉમેરીએ—કાર્યો જે ડોમેન-વિશિષ્ટ ક્રિયાઓનું પ્રતિનિધિત્વ કરે છે.
// ... inside the makeUser function ...
if (!password || password.length < 8) {
// ...
}
const passwordHash = hashPassword(password);
const normalizedEmail = email.toLowerCase();
return Object.freeze({
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => normalizedEmail,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt,
// Business Logic / Behavior
getFullName: () => `${firstName} ${lastName}`,
// A method that describes a business rule
canVote: () => {
// In some countries, voting age is 18. This is a business rule.
// Let's assume we have a dateOfBirth property.
const age = calculateAge(dateOfBirth);
return age >= 18;
}
});
// ...
getFullName લોજિક હવે કોઈ રેન્ડમ કંટ્રોલરમાં વિખરાયેલું નથી; તે `User` એન્ટિટીની પોતાની છે. `User` object ધરાવનાર કોઈપણ હવે `user.getFullName()` કૉલ કરીને વિશ્વસનીય રીતે સંપૂર્ણ નામ મેળવી શકે છે. લોજિક એકવાર, એક જગ્યાએ વ્યાખ્યાયિત થયેલ છે.
વ્યવહારુ ઉદાહરણ બનાવવું: એક સરળ ઈ-કોમર્સ સિસ્ટમ
ચાલો આ પેટર્નને વધુ ઇન્ટરકનેક્ટેડ ડોમેન પર લાગુ કરીએ. અમે `Product`, `OrderItem`, અને `Order` મોડેલ કરીશું.
1. `Product` Entity નું મોડેલિંગ
પ્રોડક્ટમાં નામ, કિંમત અને સ્ટોક માહિતી હોય છે. તેમાં નામ હોવું આવશ્યક છે, અને તેની કિંમત નકારાત્મક ન હોઈ શકે.
// file: /domain/product.js
export default function buildMakeProduct({ Id }) {
return function makeProduct({
id = Id.makeId(),
name,
description,
price,
stock = 0
}) {
if (!Id.isValidId(id)) {
throw new Error('Product must have a valid ID.');
}
if (!name || name.trim().length < 2) {
throw new Error('Product name must be at least 2 characters.');
}
if (isNaN(price) || price <= 0) {
throw new Error('Product must have a price greater than zero.');
}
if (isNaN(stock) || stock < 0) {
throw new Error('Stock must be a non-negative number.');
}
return Object.freeze({
getId: () => id,
getName: () => name,
getDescription: () => description,
getPrice: () => price,
getStock: () => stock,
// Business logic
isAvailable: () => stock > 0,
// A method that modifies state by returning a new instance
reduceStock: (amount) => {
if (amount > stock) {
throw new Error('Not enough stock available.');
}
// Return a NEW product object with the updated stock
return makeProduct({ id, name, description, price, stock: stock - amount });
}
});
}
}
reduceStock પદ્ધતિની નોંધ લો. આ અપરિવર્તનશીલતા સંબંધિત એક મહત્વપૂર્ણ ખ્યાલ છે. હાલના object પર `stock` property બદલવાને બદલે, તે અપડેટ કરેલા મૂલ્ય સાથે *નવો* `Product` ઇન્સ્ટન્સ પરત કરે છે. આ સ્ટેટ ફેરફારોને સ્પષ્ટ અને અનુમાનિત બનાવે છે.
2. `Order` Entity નું મોડેલિંગ (Aggregate Root)
એક `Order` વધુ જટિલ છે. તે ડોમેન-ડ્રિવન ડિઝાઇન (DDD) જે 'Aggregate Root' કહે છે તે છે. તે એક એન્ટિટી છે જે તેની સીમાની અંદર અન્ય, નાના object નું સંચાલન કરે છે. `Order` માં `OrderItem` ની સૂચિ હોય છે. તમે સીધા ઓર્ડરમાં પ્રોડક્ટ ઉમેરતા નથી; તમે `OrderItem` ઉમેરો છો જેમાં પ્રોડક્ટ અને જથ્થો હોય છે.
// file: /domain/order.js
export const ORDER_STATUS = {
PENDING: 'PENDING',
PAID: 'PAID',
SHIPPED: 'SHIPPED',
CANCELLED: 'CANCELLED'
};
export default function buildMakeOrder({ Id, validateOrderItem }) {
return function makeOrder({
id = Id.makeId(),
customerId,
items = [],
status = ORDER_STATUS.PENDING,
createdAt = new Date()
}) {
if (!Id.isValidId(id)) {
throw new Error('Order must have a valid ID.');
}
if (!customerId) {
throw new Error('Order must have a customer ID.');
}
let orderItems = [...items]; // Create a private copy to manage
return Object.freeze({
getId: () => id,
getCustomerId: () => customerId,
getItems: () => [...orderItems], // Return a copy to prevent external modification
getStatus: () => status,
getCreatedAt: () => createdAt,
// Business Logic
calculateTotal: () => {
return orderItems.reduce((total, item) => {
return total + (item.getPrice() * item.getQuantity());
}, 0);
},
addItem: (item) => {
// validateOrderItem is a function that ensures the item is a valid OrderItem entity
validateOrderItem(item);
// Business rule: prevent adding duplicates, just increase quantity
const existingItemIndex = orderItems.findIndex(i => i.getProductId() === item.getProductId());
if (existingItemIndex > -1) {
const newQuantity = orderItems[existingItemIndex].getQuantity() + item.getQuantity();
// Here you'd update the quantity on the existing item
// (This requires items to be mutable or have an update method)
} else {
orderItems.push(item);
}
},
markPaid: () => {
if (status !== ORDER_STATUS.PENDING) {
throw new Error('Only pending orders can be marked as paid.');
}
// Return a new Order instance with the updated status
return makeOrder({ id, customerId, items: orderItems, status: ORDER_STATUS.PAID, createdAt });
}
});
}
}
આ `Order` એન્ટિટી હવે જટિલ વ્યવસાય નિયમો લાગુ કરે છે:
- તે તેના પોતાના વસ્તુઓની સૂચિનું સંચાલન કરે છે.
- તે તેના પોતાના કુલની ગણતરી કરવાનું જાણે છે.
- તે સ્ટેટ ટ્રાન્ઝિશન લાગુ કરે છે (દા.ત., તમે ફક્ત `PENDING` ઓર્ડરને `PAID` તરીકે ચિહ્નિત કરી શકો છો).
ઓર્ડર માટેનો બિઝનેસ લોજિક હવે આ મોડ્યુલની અંદર સુઘડ રીતે એન્કેપ્સ્યુલેટ થયેલ છે, અલગથી પરીક્ષણક્ષમ છે, અને તમારી સમગ્ર એપ્લિકેશનમાં પુનઃઉપયોગી છે.
અદ્યતન પેટર્ન અને વિચારણાઓ
Immutability: અનુમાનિતતાનો આધારસ્તંભ
અમે અપરિવર્તનશીલતા પર સ્પર્શ કર્યો છે. તે આટલું મહત્વપૂર્ણ શા માટે છે? જ્યારે object અપરિવર્તનશીલ હોય, ત્યારે તમે તેમને તમારી એપ્લિકેશનમાં પસાર કરી શકો છો તે ડર વિના કે કોઈ દૂરનું કાર્ય તેમની સ્થિતિને અણધારી રીતે બદલશે. આ બગ્સની સંપૂર્ણ શ્રેણીને દૂર કરે છે અને તમારી એપ્લિકેશનના ડેટા ફ્લોને સમજવા માટે વધુ સરળ બનાવે છે.
Object.freeze() એક છીછરા ઠંડું પ્રદાન કરે છે. nested object અથવા arrays (જેમ કે અમારા `Order`) ધરાવતી એન્ટિટીઝ માટે, તમારે વધુ સાવચેત રહેવું પડશે. ઉદાહરણ તરીકે, `order.getItems()` માં, અમે કૉલરને ઓર્ડરના આંતરિક એરેમાં વસ્તુઓ સીધી પુશ કરવાથી રોકવા માટે કોપી (`[...orderItems]`) પરત કરી.
જટિલ એપ્લિકેશનો માટે, Immer જેવી લાઇબ્રેરીઓ nested structures ને અપરિવર્તનશીલ બનાવવાનું સરળ બનાવી શકે છે, પરંતુ મુખ્ય સિદ્ધાંત યથાવત રહે છે: તમારી એન્ટિટીઝને અપરિવર્તનશીલ મૂલ્યો તરીકે વર્તે. જ્યારે ફેરફાર કરવાની જરૂર હોય, ત્યારે નવું મૂલ્ય બનાવો.
Asynchronous Operations અને Persistence નું હેન્ડલિંગ
તમે અમારી એન્ટિટીઝ સંપૂર્ણપણે સિંક્રનસ જોઈ હશે. તેમને ડેટાબેસેસ અથવા API વિશે કંઈપણ ખબર નથી. આ હેતુપૂર્વક છે અને પેટર્નની મોટી શક્તિ છે!
Entities એ પોતાને સાચવવી જોઈએ નહીં. એન્ટિટીનું કાર્ય બિઝનેસ નિયમો લાગુ કરવાનું છે. ડેટાબેઝમાં ડેટા સાચવવાનું કાર્ય તમારા એપ્લિકેશનના એક અલગ સ્તરનું છે, જેને ઘણીવાર Service Layer, Use Case Layer, અથવા Repository Pattern કહેવાય છે.
તેઓ કેવી રીતે સંપર્ક કરે છે:
// file: /use-cases/create-user.js
// This use case depends on the user entity factory and a database access function.
export default function makeCreateUser({ makeUser, usersDatabase }) {
return async function createUser(userInfo) {
// 1. Create a valid domain entity. This step validates the data.
const user = makeUser(userInfo);
// 2. Check for business rules that require external data (e.g., email uniqueness)
const exists = await usersDatabase.findByEmail({ email: user.getEmail() });
if (exists) {
throw new Error('Email address is already in use.');
}
// 3. Persist the entity. The database needs a plain object.
const persisted = await usersDatabase.insert({
id: user.getId(),
firstName: user.getFirstName(),
// ... and so on
});
return persisted;
}
}
ચિંતાઓની આ વિભાજન શક્તિશાળી છે:
- `User` એન્ટિટી શુદ્ધ, સિંક્રનસ અને એકમ પરીક્ષણ કરવા માટે સરળ છે.
- `createUser` ઉપયોગ-કેસ ઓર્કેસ્ટ્રેશન માટે જવાબદાર છે અને મોક ડેટાબેઝ સાથે એકીકરણ-પરીક્ષણ કરી શકાય છે.
- `usersDatabase` મોડ્યુલ ચોક્કસ ડેટાબેઝ ટેકનોલોજી માટે જવાબદાર છે અને તેને અલગથી પરીક્ષણ કરી શકાય છે.
Serialization અને Deserialization
તમારી એન્ટિટીઝ, તેમના કાર્યો સાથે, સમૃદ્ધ object છે. પરંતુ જ્યારે તમે નેટવર્ક પર ડેટા મોકલો છો (દા.ત., JSON API પ્રતિભાવમાં) અથવા તેને ડેટાબેઝમાં સ્ટોર કરો છો, ત્યારે તમને સાદા ડેટા પ્રતિનિધિત્વની જરૂર હોય છે. આ પ્રક્રિયાને serialization કહેવામાં આવે છે.
એક સામાન્ય પેટર્ન એ છે કે તમારી એન્ટિટીમાં `toJSON()` અથવા `toObject()` પદ્ધતિ ઉમેરવી.
// ... inside the makeUser function ...
return Object.freeze({
getId: () => id,
// ... other getters
// Serialization method
toObject: () => ({
id,
firstName,
lastName,
email: normalizedEmail,
createdAt
// Notice we don't include the passwordHash
})
});
વિપરીત પ્રક્રિયા, ડેટાબેઝ અથવા API માંથી સાદા ડેટા લેવા અને તેને ફરીથી સમૃદ્ધ ડોમેન એન્ટિટીમાં ફેરવવું, તે બરાબર તે જ છે જે તમારું `makeUser` ફેક્ટરી કાર્ય છે. આ deserialization છે.
TypeScript અથવા JSDoc સાથે ટાઇપિંગ
જ્યારે આ પેટર્ન વેનીલા JavaScript માં સંપૂર્ણ રીતે કાર્ય કરે છે, ત્યારે TypeScript અથવા JSDoc સાથે સ્ટેટિક પ્રકારો ઉમેરવાથી તેને સુપરચાર્જ થાય છે. પ્રકારો તમને તમારી એન્ટિટીના 'આકાર' ને ઔપચારિક રીતે વ્યાખ્યાયિત કરવાની મંજૂરી આપે છે, ઉત્તમ ઓટોકમ્પલેશન અને કમ્પાઇલ-ટાઇમ તપાસ પ્રદાન કરે છે.
// file: /domain/user.ts
// Define the entity's public interface
export type User = Readonly<{
getId: () => string;
getFirstName: () => string;
// ... etc
getFullName: () => string;
}>;
// The factory function now returns the User type
export default function buildMakeUser(...) {
return function makeUser(...): User {
// ... implementation
}
}
Module Entity Pattern ના સર્વગ્રાહી લાભો
આ પેટર્નને અપનાવીને, તમે અનેક લાભો મેળવો છો જે તમારી એપ્લિકેશન વધવાની સાથે વધે છે:
- સત્યનો એકમાત્ર સ્ત્રોત: બિઝનેસ નિયમો અને ડેટા માન્યતા કેન્દ્રિત અને અસ્પષ્ટ છે. નિયમમાં ફેરફાર બરાબર એક જગ્યાએ કરવામાં આવે છે.
- ઉચ્ચ સહયોગ, ઓછું જોડાણ: એન્ટિટીઝ સ્વયં-સમાયેલ છે અને બહારની સિસ્ટમો પર આધાર રાખતી નથી. આ તમારી કોડબેઝને મોડ્યુલર બનાવે છે અને રિફેક્ટર કરવાનું સરળ બનાવે છે.
- સુપ્રીમ પરીક્ષણક્ષમતા: તમે સમગ્ર વિશ્વને મોક કર્યા વિના તમારા સૌથી નિર્ણાયક બિઝનેસ લોજિક માટે સરળ, ઝડપી એકમ પરીક્ષણો લખી શકો છો.
- સુધારેલ ડેવલપર અનુભવ: જ્યારે કોઈ ડેવલપરને `User` સાથે કામ કરવાની જરૂર હોય, ત્યારે તેમની પાસે ઉપયોગ કરવા માટે એક સ્પષ્ટ, અનુમાનિત અને સ્વ-દસ્તાવેજીકૃત API હોય છે. હવે પ્લેન object ના આકારનું અનુમાન લગાવવાની જરૂર નથી.
- સ્કેલેબિલિટી માટેનો પાયો: આ પેટર્ન તમને સ્થિર, વિશ્વસનીય કોર આપે છે. જેમ તમે વધુ સુવિધાઓ, ફ્રેમવર્ક અથવા UI ઘટકો ઉમેરો છો, તેમ તમારું બિઝનેસ લોજિક સુરક્ષિત અને સુસંગત રહે છે.
નિષ્કર્ષ: તમારી એપ્લિકેશન માટે એક મજબૂત કોર બનાવો
ઝડપથી બદલાતા ફ્રેમવર્ક અને લાઇબ્રેરીઓની દુનિયામાં, એ ભૂલી જવું સરળ છે કે આ સાધનો ક્ષણિક છે. તેઓ બદલાશે. જે ટકી રહે છે તે તમારા વ્યવસાય ડોમેનનું મુખ્ય લોજિક છે. આ ડોમેનને યોગ્ય રીતે મોડેલ કરવામાં સમય રોકાણ ફક્ત એક શૈક્ષણિક કવાયત નથી; તે તમારા સોફ્ટવેરના સ્વાસ્થ્ય અને દીર્ધાયુષ્યમાં તમે કરી શકો તેવા સૌથી નોંધપાત્ર લાંબા ગાળાના રોકાણોમાંનું એક છે.
JavaScript Module Entity Pattern આ વિચારોને અમલમાં મૂકવા માટે એક સરળ, શક્તિશાળી અને મૂળ રીત પ્રદાન કરે છે. તેને ભારે ફ્રેમવર્ક અથવા જટિલ સેટઅપની જરૂર નથી. તે તમને સ્વચ્છ, સ્થિતિસ્થાપક અને સમજી શકાય તેવા એપ્લિકેશન કોર બનાવવા માટે મદદ કરવા માટે ભાષાની મૂળભૂત સુવિધાઓ—મોડ્યુલ્સ, કાર્યો અને ક્લોઝર્સ—નો લાભ લે છે. તમારા આગામી પ્રોજેક્ટમાં એક મુખ્ય એન્ટિટીથી શરૂઆત કરો. તેની પ્રોપર્ટીઝનું મોડેલિંગ કરો, તેના નિર્માણને માન્ય કરો, અને તેને વર્તણૂક આપો. તમે વધુ મજબૂત અને વ્યાવસાયિક સોફ્ટવેર આર્કિટેક્ચર તરફ પ્રથમ પગલું ભરી રહ્યા હશો.