SoC (Separation of Concerns) হল সেই নীতির সংক্ষিপ্ত নাম যার মাধ্যমে সফটওয়্যার সিস্টেমকে দায়িত্বের বিচ্ছিন্ন এলাকায় ভাগ করা হয়। Martin Fowler-এর মতে, দায়িত্ব বিভাজন রক্ষণাবেক্ষণযোগ্য কোডের একটি মূল উপাদান। নীতি SoC ডেভেলপারদের অ্যাপ্লিকেশনের একটি স্তর পরিবর্তন করতে দেয় অন্যগুলোকে প্রভাবিত না করে, যা টিম মোবাইল ডেভেলপমেন্টে বিশেষভাবে গুরুত্বপূর্ণ।
মূল বিষয়
SoC-এর পূর্ণ নাম Separation of Concerns — «দায়িত্ব বিভাজন» বা «আগ্রহের এলাকার বিভাজন»। ডেভেলপমেন্টের প্রসঙ্গে, concern শব্দটি যেকোনো পৃথকযোগ্য কার্যকারিতা নির্দেশ করে: ইউজার ইন্টারফেস প্রদর্শন, ক্লিক প্রক্রিয়াকরণ, ডেটা যাচাইকরণ, নেটওয়ার্ক যোগাযোগ বা ডেটাবেস নিয়ে কাজ। SoC নীতি কোডকে এই এলাকাগুলোর চারপাশে গ্রুপ করার নির্দেশ দেয় যাতে একটির পরিবর্তন অন্যগুলোকে প্রভাবিত না করে।
সংক্ষিপ্ত নাম SoC প্রযুক্তিগত সাহিত্য, আর্কিটেকচার আলোচনা এবং ফ্রেমওয়ার্ক ডকুমেন্টেশনে ব্যাপকভাবে ব্যবহৃত হয়। উদাহরণস্বরূপ, Android Architecture Components-এর ডকুমেন্টেশনে ViewModel এবং View আলাদা করার প্রেরণা হিসেবে SoC-র বারবার উল্লেখ করা হয়েছে। iOS সম্প্রদায়ে, Massive View Controller সমস্যা নিয়ে আলোচনায় শব্দটি ব্যবহৃত হয় — যা SoC-র অভাবের সরাসরি ফলাফল।
এটা বোঝা গুরুত্বপূর্ণ যে SoC একটি এককালীন কাজ নয়, বরং একটি ধারাবাহিক প্রক্রিয়া। অ্যাপ্লিকেশন বাড়ার সাথে সাথে দায়িত্বের নতুন এলাকা দেখা দেয় এবং আর্কিটেকচার পুনর্বিবেচনা করতে হয়। একটি ভাল কোডবেস স্থিতিশীল অবস্থায় পৌঁছানোর আগে বিভাজনের বেশ কয়েকটি পুনরাবৃত্তির মধ্য দিয়ে যায় যেখানে প্রতিটি concern বিচ্ছিন্ন এবং পরিচালনাযোগ্য।
Separation of Concerns এবং এর সংক্ষিপ্ত নাম SoC একই নীতি নির্দেশ করে। পার্থক্য শুধু ব্যবহারের প্রসঙ্গে: পূর্ণ নামটি আনুষ্ঠানিক ডকুমেন্ট, শিক্ষামূলক উপকরণ এবং নতুন ডেভেলপারদের প্রথমবার ধারণা ব্যাখ্যা করার সময় ব্যবহৃত হয়। SoC প্রযুক্তিগত আলোচনা, কোড রিভিউ এবং ডকুমেন্টেশনে সুবিধাজনক যেখানে সংক্ষিপ্ততা গুরুত্বপূর্ণ।
পেশাদার পরিবেশে, দুটি শব্দই পরস্পর বিনিময়যোগ্য। একজন ডেভেলপার বলতে পারেন «এখানে SoC লঙ্ঘিত হয়েছে» বা «এটি Separation of Concerns লঙ্ঘন করে» — অর্থ পরিবর্তন হয় না। তবে, চাকরির বিজ্ঞাপন এবং আর্কিটেকচার প্রয়োজনীয়তায় পূর্ণ নাম বেশি ব্যবহৃত হয়, যেখানে চ্যাট এবং কোড রিভিউতে সংক্ষিপ্ত নাম ব্যবহৃত হয়। শিল্পে স্বাচ্ছন্দ্যে প্রবেশের জন্য উভয় বিকল্পের জ্ঞান প্রয়োজন।
এখানে শব্দগত বিভ্রান্তি রয়েছে: সংক্ষিপ্ত নাম SoC হার্ডওয়্যার প্রসঙ্গে System-on-a-Chip (চিপে সিস্টেম) জন্যও ব্যবহৃত হয়। মোবাইল ডেভেলপমেন্টে, প্রসঙ্গ সবসময় পরিবেশ থেকে স্পষ্ট — যদি আলোচনা কোড আর্কিটেকচার নিয়ে হয়, তবে তা Separation of Concerns-কে নির্দেশ করে। এই নিবন্ধে, SoC সর্বদা দায়িত্ব বিভাজনের নীতিকে নির্দেশ করে।
তিন-স্তর বিশিষ্ট আর্কিটেকচার মোবাইল অ্যাপ্লিকেশনে SoC বাস্তবায়নের সবচেয়ে সাধারণ উপায়। এটি কোডকে Presentation (UI), Domain (ব্যবসায়িক লজিক) এবং Data (ডেটা উৎস) এ ভাগ করে। প্রতিটি স্তরে কঠোরভাবে সংজ্ঞায়িত শ্রেণির ধরন থাকে এবং ইন্টারফেসের মাধ্যমে প্রতিবেশীদের থেকে বিচ্ছিন্ন থাকে। এই পদ্ধতি iOS, Android এবং Flutter প্রকল্পের জন্য সমানভাবে কার্যকর।
View এবং ViewModel প্রেজেন্টেশন স্তর গঠন করে। View ইন্টারফেস রেন্ডার এবং ব্যবহারকারীর ঘটনা প্রেরণের জন্য দায়ী। ViewModel স্ক্রিনের অবস্থা ধারণ করে এবং Domain স্তর থেকে ডেটা প্রদর্শনের জন্য প্রস্তুত ফর্ম্যাটে রূপান্তর করে। ViewModel-এ Activity, Fragment বা UIViewController-এর কোনো রেফারেন্স নেই — এটি UI এবং লজিকের মধ্যে SoC নিশ্চিত করে।
উদাহরণস্বরূপ, Android Jetpack-এ, ViewModel স্ক্রিন রোটেশন থেকে বেঁচে যায় যখন UI পুনরায় তৈরি হয়। SoC ছাড়া, Activity-তে অবস্থা সংরক্ষণ করতে হত, লাইফসাইকেল ব্যবস্থাপনাকে ডেটার সাথে মিশিয়ে। ViewModel এই সমস্যাটি বিচ্ছিন্নভাবে সমাধান করে, দায়িত্ব বিভাজনের নীতির একটি পরিষ্কার বাস্তবায়ন প্রদর্শন করে।
Use Cases-এ প্ল্যাটফর্ম-স্বাধীন ব্যবসায়িক নিয়ম থাকে। এই স্তর Android SDK, iOS UIKit বা Flutter framework আমদানি করে না। Use Case Repository থেকে ডেটা গ্রহণ করে, এতে ব্যবসায়িক লজিক প্রয়োগ করে এবং ফলাফল ফেরত দেয়। SoC-র কারণে, একটি Use Case বিভিন্ন স্ক্রিন এবং প্ল্যাটফর্মে পুনরায় ব্যবহার করা যেতে পারে।
একটি ক্লাসিক উদাহরণ রেজিস্ট্রেশন ফর্মের জন্য ValidateAndSaveUseCase। এটি ইমেল এবং পাসওয়ার্ডের বৈধতা检查和 করে, সংরক্ষণের জন্য UserRepository কল করে এবং ValidationResult ফেরত দেয়। UI বা ডেটাবেস কেউই যাচাইকরণ নিয়ম জানে না — তারা এক জায়গায় কেন্দ্রীভূত, যা তাদের পরিবর্তন করা সহজ করে।
Repository ডেটা উৎসগুলিকে বাকি অ্যাপ্লিকেশন থেকে পৃথক করে। ViewModel জানে না ডেটা কোথা থেকে আসে — REST API, GraphQL, লোকাল ডেটাবেস বা ক্যাশ থেকে। Repository সিদ্ধান্ত নেয় কোন উৎস ব্যবহার করতে হবে এবং এই লজিকটি একটি ইন্টারফেসের আড়ালে লুকিয়ে রাখে। এটি ডেটা সংগ্রহের এবং ব্যবহারের মধ্যে SoC।
DataSource আরও গভীর বিভাজন প্রদান করে: RemoteDataSource শুধু HTTP অনুরোধের জন্য দায়ী, LocalDataSource Room, CoreData বা SharedPreferences-এর সাথে কাজ করার জন্য দায়ী। Repository ক্যাশিং কৌশল প্রয়োগ করে তাদের সংযুক্ত করে। প্রতিটি DataSource স্বাধীনভাবে প্রতিস্থাপন করা যেতে পারে, যা সার্ভার বা ডেটাবেসের মধ্যে মাইগ্রেট করার সময় গুরুত্বপূর্ণ।
DataSource-এর এই বহু-স্তরের সিস্টেম অবকাঠামো স্তরে SoC বাস্তবায়ন করে: নেটওয়ার্ক যোগাযোগ, লোকাল স্টোরেজ এবং ক্যাশিং পৃথক concerns, প্রতিটির নিজস্ব লজিক এবং লাইফসাইকেল রয়েছে। HTTP ক্লায়েন্ট প্রতিস্থাপন করার সময়, শুধু RemoteDataSource পরিবর্তিত হয়, যখন Repository এবং উচ্চতর স্তরগুলি অপরিবর্তিত থাকে, যা দায়িত্ব বিভাজনের ব্যবহারিক মূল্য নিশ্চিত করে।
MVP (Model-View-Presenter) মোবাইল ডেভেলপমেন্টে স্পষ্টভাবে SoC বাস্তবায়নকারী প্রথম প্যাটার্নগুলির মধ্যে একটি ছিল। Presenter-এ লজিক থাকে এবং একটি ইন্টারফেসের মাধ্যমে View নিয়ন্ত্রণ করে। View নিষ্ক্রিয় — এটি শুধু Presenter যা বলে তা প্রদর্শন করে। বিভাজন টেস্টিং সহজ করে: Presenter এমুলেটর ছাড়া পরীক্ষা করা হয়, এবং View এত সহজ থাকে যে এতে ভাঙার কিছু নেই।
MVVM প্রতিক্রিয়াশীল বাইন্ডিং যোগ করেছে: View Observable বা StateFlow-এর মাধ্যমে ViewModel-এ পরিবর্তনের সাবস্ক্রাইব করে। ViewModel View-এর রেফারেন্স ধারণ করে না, যা মেমরি লিকের ঝুঁকি দূর করে এবং concerns-কে আরও আলাদা করে। Android-এ, Jetpack ViewModel এবং LiveData-এর কারণে MVVM মানক হয়ে গেছে, iOS-এ — Combine এবং RxSwift-এর কারণে।
Clean Architecture রবার্ট মার্টিনের SoC-কে রিং-এ আমূল বিভাজনে নিয়ে যায়। বাইরের রিং (ফ্রেমওয়ার্ক এবং ড্রাইভার) ভিতরের (এন্টিটি) উপর নির্ভর করে, কিন্তু উল্টো নয়। বাস্তবে, মোবাইল প্রকল্পগুলি খুব কমই চারটি রিং বাস্তবায়ন করে — Presentation-এর চারপাশে Domain এবং Data স্তর যথেষ্ট। কিন্তু «ভিতরের দিকে নির্ভরতা» নীতি ফ্রেমওয়ার্ক পরিবর্তন করার সময় উল্লেখযোগ্য সুবিধা দেয়।
// View — শুধু প্রদর্শন, কোনো লজিক নেই
final class LoginViewController: UIViewController {
let viewModel: LoginViewModel
func loginTapped() {
viewModel.login(emailField.text, passwordField.text)
}
}
// ViewModel — স্ক্রিন লজিক ধারণ করে, UIKit জানে না
final class LoginViewModel {
private let loginUseCase: LoginUseCase
func login(email: String?, password: String?) {
loginUseCase.execute(email, password)
}
}
// Use Case — ব্যবসায়িক লজিক, প্ল্যাটফর্ম-স্বাধীন
final class LoginUseCase {
private let repo: AuthRepository
func execute(email: String?, password: String?) {
guard let e = email, let p = password else { return }
repo.authenticate(e, p)
}
}
উদাহরণটি SoC-র তিনটি স্তর দেখায়: LoginViewController শুধু ঘটনা প্রেরণ করে, LoginViewModel অবস্থা পরিচালনা করে, LoginUseCase ব্যবসায়িক নিয়ম ধারণ করে। প্রতিটি শ্রেণি স্বাধীনভাবে পরীক্ষা করা হয়, এবং UI ফ্রেমওয়ার্ক পরিবর্তন Use Case-কে প্রভাবিত করে না।
Massive View Controller iOS-এ SoC-র সবচেয়ে সাধারণ লঙ্ঘন। একটি শ্রেণি যা UI পরিচালনা করে, নেটওয়ার্ক অনুরোধ প্রক্রিয়া করে, JSON পার্স করে এবং ডেটা সংরক্ষণ করে, সব স্তরে নীতি লঙ্ঘন করে। সমাধান হল প্রতিটি দায়িত্ব আলাদা কম্পোনেন্টে নিষ্কাশন করা: NetworkingService, JSONParser, CoreDataStack, ViewController-কে শুধু View পরিচালনার জন্য রেখে।
Android-এ অনুরূপ সমস্যা হল God Activity বা God Fragment। একটি অ্যাক্টিভিটি যা ডেটা লোড করে, ফর্ম বৈধতা দেয়, ডায়ালগ দেখায় এবং UI আপডেট করে। এটি ViewModel এবং Repository প্রবর্তনের মাধ্যমে চিকিৎসা করা হয়, যা অবস্থা পরিচালনা এবং ডেটা নিজেদের উপর নেয়। ViewModel স্ক্রিন রোটেশনের সময় ডেটা হারানো থেকেও রক্ষা করে।
তৃতীয় লঙ্ঘন হল প্ল্যাটফর্ম এবং ব্যবসায়িক কোড মিশ্রিত করা। উদাহরণস্বরূপ, SwiftUI View বা Android Composable-এ সরাসরি HTTP অনুরোধ রাখা। এটি কোডকে অপরিবর্তনীয় এবং পরীক্ষা করা কঠিন করে তোলে। সঠিক পদ্ধতি হল অনুরোধটি Repository-তে স্থানান্তর করা, যা Use Case-এর মাধ্যমে কল করা হয়, যখন View শুধু ফলাফলে সাবস্ক্রাইব করে। সিস্টেমের প্রতিটি উপাদান নিজের কাজ সমাধান করে এবং নিজের সীমানার বাইরে যায় না।
সচরাচর জিজ্ঞাসিত প্রশ্ন
না। SoC একটি আরও সাধারণ নীতি যা সিস্টেমকে দায়িত্বের এলাকায় ভাগ করে। SOLID হল অবজেক্ট-ওরিয়েন্টেড ডিজাইনের জন্য পাঁচটি নির্দিষ্ট নিয়মের একটি সেট। SOLID-এর প্রথম নীতি (Single Responsibility) একটি একক শ্রেণির স্তরে SoC-র একটি বিশেষ ক্ষেত্র।
পরিবর্তনের জন্য এক কারণের নিয়ম (Single Responsibility) ব্যবহার করুন। যদি একটি শ্রেণি UI, ডেটা ফর্ম্যাট এবং ব্যবসায়িক নিয়মে পরিবর্তনের কারণে পরিবর্তিত হয় — SoC লঙ্ঘিত হয়েছে। ArchTest (Android) এবং StrictConcurrency (iOS)-এর মতো সরঞ্জামগুলি স্বয়ংক্রিয়ভাবে এই ধরনের লঙ্ঘন সনাক্ত করতে সাহায্য করে।
তত্ত্বগতভাবে, অতিরিক্ত স্তরগুলি পরোক্ষ কল যোগ করে, কিন্তু বাস্তবে মোবাইল অ্যাপ্লিকেশনের কর্মক্ষমতার উপর প্রভাব নগণ্য। কম্পাইলার অনেক কল ইনলাইন করে, এবং JIT এবং AOT অপ্টিমাইজেশন ওভারহেড দূর করে। কোডের রক্ষণাবেক্ষণযোগ্যতা বিমূর্ততার চেয়ে অনেক বেশি লাভ দেয়।
UI থেকে নেটওয়ার্ক অনুরোধগুলি নিষ্কাশন করে Repository-তে শুরু করুন। তারপর ব্যবসায়িক লজিক Use Cases-এ স্থানান্তর করুন। স্তরগুলি সংযোগ করতে ডিপেন্ডেন্সি ইঞ্জেকশন ব্যবহার করুন। পরিবর্তনগুলি পুনরাবৃত্তিমূলকভাবে করুন, নতুন কোড টেস্ট দিয়ে কভার করুন — এটি নিশ্চিত করে যে রিফ্যাক্টরিং বিদ্যমান কার্যকারিতা ভাঙে না।
প্রোটোটাইপ-এ গতির জন্য SoC লঙ্ঘন করা যেতে পারে। কিন্তু যদি একটি প্রোটোটাইপ প্রোডাক্ট ডেভেলপমেন্টে যায়, রিফ্যাক্টরিংয়ের খরচ দ্রুত শুরুর সুবিধাকে ছাড়িয়ে যেতে পারে। সর্বোত্তম হল প্রোটোটাইপেও ন্যূনতম বিভাজন (UI এবং ডেটা) বজায় রাখা যাতে লঞ্চের সময় সবকিছু শুরু থেকে লিখতে না হয়।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন