Tale of Software Architect(ure): Part 8 (Architecture Patterns and Layered Architecture)
Saiful Islam Rasel
Senior Engineer, SDE @ bKash | Ex: AsthaIT | Sports Programmer | Problem Solver | FinTech | Microservice | Java | Spring-boot | C# | .NET | PostgreSQL | DynamoDB | JavaScript | TypeScript | React.js | Next.js | Angular
Story:
One day, a customer enters the restaurant. The host greets them and shows them to a table. The waiter arrives, takes their order, and passes it to the chef. The chef checks with the stockroom manager to ensure they have all the ingredients. The stockroom, in turn, regularly orders fresh supplies from the supplier to keep the kitchen well-stocked. The waiter brings the finished meal to the customer, who enjoys a perfect dining experience.
Each person in the restaurant has a clear role. The host doesn’t cook, the waiter doesn’t manage stock, and the chef doesn’t greet customers. Each layer focuses on its own responsibilities, ensuring smooth and efficient operation in a sequential manner.
Architecture Patterns & Its Category:
Patterns
Architectural patterns are specific, reusable solutions to common architectural problems. Its establishes a relationship between:
Category
Similarity and Difference of Architecture Styles and Patterns:
You can think of architecture styles as the big-picture strategy and architecture patterns as detailed tactics used within that strategy.
Layered (N-Tier) Architecture:
Layered architecture is a software design pattern that structures an application into multiple layers, each with distinct responsibilities. This design promotes separation of concerns, making systems easier to develop, test, and maintain. In a typical layered architecture, each layer only interacts with its adjacent lower layers, creating a clean and manageable structure.
Context
Layered architecture is commonly applied in building large, scalable, and maintainable software systems that require clear separation between different parts of the system. It is often used in enterprise applications, web systems, and mobile apps, where different components such as user interfaces, business logic, and databases need to work together seamlessly.
In such environments:
Problem
Without a clear structure, software systems can become giant and hard to manage:
Solution
The Layered Architecture addresses these problems by breaking the system into distinct layers, each with a clear role:
Separation of Concerns:
Loose Coupling:
Reusability:
Testability:
Example Solution
In a hotel reservation system:
Context:
The system needs to manage customer reservations, handle payments, provide an interface for guests to browse available rooms, and store booking data in a database.
Problem:
Without clear separation, the code that handles room availability, customer interaction, and payment processing would be mixed together, making it difficult to modify, debug, or scale as the system grows.
Layered Solution:
By using layered architecture, each part of the system can evolve independently, be tested separately, and scale as needed without affecting other parts of the application.
Pseudocode:
1. Presentation Layer (UI Layer)
function displayAvailableRooms():
checkInDate = getUserInput("Enter check-in date:")
checkOutDate = getUserInput("Enter check-out date:")
availableRooms = ReservationService.findAvailableRooms(checkInDate, checkOutDate)
if availableRooms is empty:
print("No rooms available for the selected dates.")
else:
print("Available Rooms: ", availableRooms)
roomSelection = getUserInput("Select a room:")
ReservationService.bookRoom(roomSelection, checkInDate, checkOutDate)
function getUserInput(prompt):
print(prompt)
return userInput()
2. Application Layer (Service Layer)
class ReservationService:
function findAvailableRooms(checkInDate, checkOutDate):
return RoomAvailabilityService.getAvailableRooms(checkInDate, checkOutDate)
function bookRoom(roomId, checkInDate, checkOutDate):
if RoomAvailabilityService.isRoomAvailable(roomId, checkInDate, checkOutDate):
totalCost = BookingService.calculateTotalCost(roomId, checkInDate, checkOutDate)
BookingService.createBooking(roomId, checkInDate, checkOutDate, totalCost)
print("Room booked successfully!")
else:
print("Room is not available for the selected dates.")
3. Business Logic Layer (Domain Layer)
class RoomAvailabilityService:
function getAvailableRooms(checkInDate, checkOutDate):
return RoomRepository.findAvailableRooms(checkInDate, checkOutDate)
function isRoomAvailable(roomId, checkInDate, checkOutDate):
return RoomRepository.isRoomAvailable(roomId, checkInDate, checkOutDate)
class BookingService:
function calculateTotalCost(roomId, checkInDate, checkOutDate):
room = RoomRepository.getRoomById(roomId)
numberOfNights = calculateNights(checkInDate, checkOutDate)
return room.pricePerNight * numberOfNights
function createBooking(roomId, checkInDate, checkOutDate, totalCost):
BookingRepository.saveBooking(roomId, checkInDate, checkOutDate, totalCost)
4. Data Access Layer (Persistence Layer)
class RoomRepository:
function findAvailableRooms(checkInDate, checkOutDate):
query = "SELECT * FROM rooms WHERE room_id NOT IN (SELECT room_id FROM bookings WHERE check_in_date < checkOutDate AND check_out_date > checkInDate)"
return Database.execute(query)
function isRoomAvailable(roomId, checkInDate, checkOutDate):
query = "SELECT * FROM rooms WHERE room_id = roomId AND room_id NOT IN (SELECT room_id FROM bookings WHERE check_in_date < checkOutDate AND check_out_date > checkInDate)"
return Database.execute(query)
function getRoomById(roomId):
query = "SELECT * FROM rooms WHERE room_id = roomId"
return Database.execute(query)
class BookingRepository:
function saveBooking(roomId, checkInDate, checkOutDate, totalCost):
query = "INSERT INTO bookings (room_id, check_in_date, check_out_date, total_cost) VALUES (roomId, checkInDate, checkOutDate, totalCost)"
Database.execute(query)
5. Database Layer (Data Storage)
class Database:
function execute(query):
# Simulated database query execution
# In a real implementation, this would involve running the query against the database and returning results
print("Executing query: ", query)
return result
Summary:
Layered architecture is a software design pattern that organizes an application into distinct layers, each responsible for specific tasks. This separation of concerns makes the system easier to develop, maintain, and scale.
Key Layers: