- Introduction
- Features
- System Architecture
- Database Setup
- Integration Guidelines
- Usage Instructions
- Contact Information
The Book Inventory System is designed to manage a collection of books using two database options: Microsoft Access and SQL Server. The system allows users to perform key operations such as adding, updating, removing, and filtering books. Throughout the development of this system, I applied several software engineering principles to ensure the architecture is both maintainable and scalable. The solution is built with Object-Oriented Programming (OOP) principles, utilizing design patterns like the Repository Pattern and Command Pattern to decouple business logic from database operations.
This project demonstrates my ability to work with different database technologies, handle backend logic, and implement a well-structured system that is easily extensible.
The Book Inventory System incorporates several core features that demonstrate advanced backend development techniques. For example, the CRUD operations for managing books are implemented using the Repository Pattern. This abstraction allows for a clean separation between the application logic and the database, making the code easier to maintain and extend. Additionally, advanced filtering options are provided for querying books based on multiple criteria such as title, genre, and author. This flexibility is a direct result of applying the Specification Pattern, which allows dynamic query building.
One of the critical elements of the system is its multi-database support. By providing the option to work with both Microsoft Access and SQL Server, I ensured that the system could operate in different environments, highlighting my ability to handle diverse database setups. This feature demonstrates an understanding of database abstraction and how to configure different connection strings based on the database in use.
The system is built around a Repository Pattern, which provides a clear separation between the business logic and the data access layer. The Book Repository acts as the central hub for interacting with the database, offering an API that abstracts away the complexities of database interactions. This ensures that any future changes to the underlying data store (such as switching to a different database engine) will not impact the rest of the system.
I used the Command Pattern to encapsulate the different database operations, such as adding, updating, or removing books. This approach allows the application to handle operations dynamically by selecting the appropriate command based on the database engine being used. By decoupling the commands from the client code, this pattern also promotes flexibility and extensibility. For instance, should additional databases need to be supported in the future, new command classes can be added without altering the core logic.
In addition, I leveraged the Strategy Pattern for database command selection, where a DBCommandFactory dynamically chooses the correct command strategy (such as SQLServerCommandStrategy or AccessCommandStrategy) based on the database being used. This ensures that the system can easily scale to support more database systems with minimal modifications.
The system is designed around a Repository Pattern for managing the interactions with the database. The Command Pattern is used to encapsulate database queries and operations. The system is divided into two primary layers:
- Client Layer: This represents the application layer that interacts with the user, gathers inputs, and uses the Book Repository to fetch or modify data.
- Backend Layer: This includes the database, its queries, and the logic for interacting with it.
The BookRepository acts as the central hub for CRUD operations, with separate commands to handle different tasks. These tasks include adding books, updating books, filtering books based on various criteria, and removing books from the system. The commands are encapsulated in strategies that are determined by the database type (Access or SQL Server), making the system extendable.
The Database Layer consists of:
- Stored Procedures: For querying the database, ensuring that complex operations are executed efficiently at the database level.
- Functions and Triggers: Used for data integrity, such as updating the number of publications for authors when book-author relationships are changed.
- Book Repository: Handles the primary interactions with the data. It abstracts the complexity of database operations and provides a simple API for the client code.
- Commands: Different commands (Add, Update, Remove/Delete, etc.) handle the actual database interaction, which are further split based on the database engine.
- Database Service: Abstracts the connection to the database and handles opening and closing the connection.
- Factory Classes: Provide a flexible and extendable mechanism to create instances of repositories, ensuring the correct command strategies are used based on the database type.
The class diagram below illustrates the relationship between key classes and interfaces in the system.
The ERD diagram below demonstrates the relationships between the entities in the Book Inventory System, including the primary tables like Book, Author, and BookAuthor, as well as the connections between them.
- The client requests a book operation (e.g., add, remove, filter).
- The Book Repository processes the request by calling the appropriate command (e.g., AddBookCommand, RemoveBookCommand).
- The command executes the corresponding stored procedure or SQL query using the Database Service.
- The result is passed back to the client, which presents it to the user.
The Book Inventory System is built with a strong focus on maintainability, scalability, and flexibility. The following design principles have been adhered to in order to create a robust architecture:
- Each class and component in the system has a single responsibility. For example, the
BookRepositoryis responsible for interacting with the data store (i.e., database) while the command classes (AddBookCommand,FilterBooksCommand, etc.) handle specific database operations. This promotes maintainability and easier debugging.
- The system uses dependency injection to decouple classes from their dependencies, such as database services and commands. This allows for better flexibility and testability. The
BookRepositoryclass, for instance, receives its dependencies (commands for adding, updating, and filtering books) through its constructor.
- The Command Pattern is implemented to encapsulate each request as an object, thus allowing parameterization of clients with queues, requests, and operations. It also decouples the client from the system's internal workings, such as database interactions.
- For example, the command interface
IAddBookCommandis used for adding books, and different implementations are provided based on the database type (AccessAddCommand,SQLServerAddCommand).
- For example, the command interface
- The Strategy Pattern is used in the
DBCommandFactory, which selects the appropriate command strategy (AccessCommandStrategyorSQLServerCommandStrategy) based on the database type. This approach enhances flexibility by decoupling the logic for command creation, making it easy to extend the system to support additional databases in the future without modifying existing code.
- The system uses small, client-specific interfaces. For instance, the
IBookRepositoryinterface defines methods likeAddBook,RemoveBook, andFilterBooks, which are implemented in theBookRepository. Each class only depends on the methods it needs, reducing unnecessary coupling.
- Each class in the system is designed to have one reason to change. For example, the
BookRepositoryclass handles the logic for interacting with books, but it delegates database-specific operations to the command classes. The database interaction logic is contained within these command classes, adhering to SRP.
- The system is open for extension but closed for modification. New features or database engines can be added without modifying existing code, thanks to the Strategy Pattern and dependency injection. Adding support for a new database type, for instance, only requires the addition of a new command strategy without altering the existing repository logic.
- Data consistency is ensured by transactional database operations (e.g., in commands such as
AddBookandRemoveBook). This ensures that if any part of the operation fails, changes to the data are rolled back, leaving the database in a consistent state.
- The system uses centralized error handling, especially in database interactions, where all exceptions are logged and rethrown as custom exceptions. This allows for better tracking of errors and keeps the database interaction layer clean and consistent.
Now that we've covered the system's design principles, the next step is setting up the database, which forms the backbone of the Book Inventory System. Depending on your preference or project requirements, you can choose either Microsoft Access or SQL Server as your database backend. Follow the instructions below to properly configure the system with your chosen database.
To use the Book Inventory System effectively, you can choose between two database options: Microsoft Access and SQL Server. Each database type requires specific setup steps and configurations. Below is a detailed guide on how to configure the system for either database.
This section will walk you through the necessary steps to set up both environments, including updating configuration files, testing database connections, and troubleshooting common issues. Follow the instructions for your chosen database to ensure the system functions as expected.
To use the system with a Microsoft Access database, follow these steps:
-
Locate the Access Database File
The.accdbfile is provided and should be placed in the project directory. -
Set Connection String
Update the connection string in theApp.configfile to point to the correct.accdbfile. -
Testing the Connection
The application will automatically connect to the database using the provided connection string and display the results.
There are two ways to set up the SQL Server database. The first option is preferred:
-
Preferred: Using SQL Scripts
Execute the provided SQL scripts to create the necessary schema, including tables, functions, triggers, and stored procedures. These scripts are organized into the following folders. Start by executing the schema script, followed by the others:/Book-Inventory-System/SQL Server Database Files/Schema /Book-Inventory-System/SQL Server Database Files/Functions /Book-Inventory-System/SQL Server Database Files/Stored Procedures Scripts /Book-Inventory-System/SQL Server Database Files/Triggers -
Alternative: Using the Pre-Script for Additional Setup
Alternatively, you can run a single pre-configured script that handles all additional setups, including database integrity maintenance and stored procedure creation. This script can be found here:/Book-Inventory-System/Book Inventory System.sqlYou can execute this script using SQL Server Management Studio (SSMS) or Azure Data Studio to complete the setup.
Once the database setup is complete, make sure to update the connection string in the App.config file to point to the correct SQL Server instance.
Once the database is set up, the system includes a built-in testing functionality to verify the database connection. This feature ensures that the application can connect to both SQL Server and Microsoft Access, and it helps troubleshoot any connection issues. The error logging mechanism captures connection failures and provides detailed logs, which aids in diagnosing and resolving connection problems efficiently.
By incorporating this testing feature, the system ensures that the database connection is robust, reliable, and user-friendly. This also demonstrates the importance of error handling and system stability in backend development.
- Locate the compiled DLL in the
binfolder of the project. - In your application, add the DLL as a reference via Project > Add Reference > Browse.
- Ensure your project targets the same .NET framework version as the system.
-
Database Connection String:
Update yourApp.configorWeb.configfile with the appropriate connection string:- Access:
<connectionStrings> <add name="AccessDB" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=PathToYourDatabase.accdb;" /> </connectionStrings>
- SQL Server:
<connectionStrings> <add name="SQLServerDB" connectionString="Server=YourServerName;Database=BookInventory;Integrated Security=True;" /> </connectionStrings>
- Access:
-
Dependency Injection:
Use theBookRepositoryFactoryto create the repository:var result = BookRepositoryFactory.CreateBookRepository(connectionString, DatabaseType.SQLServerDB); if (result.Success) var bookRepository = result.Item;
Follow these steps to seamlessly integrate the system into your application.
This section provides step-by-step instructions for using the system to manage your book inventory. Each operation is performed through the BookRepository interface, ensuring abstraction from the underlying database logic.
To add a book with associated authors, create a Book object and pass it to the AddBook method:
var authors = new List<IAuthor>
{
AuthorFactory.CreateAuthor("John", "Doe", 3, new DateTime(1985, 7, 12)).Item,
AuthorFactory.CreateAuthor("Jane", "Smith", 5, new DateTime(1990, 4, 25)).Item
};
var book = BookFactory.CreateBook("The Great Adventure", "978-3-16-148410-0", "Fiction", 2023, authors, 10).Item;
bool success = bookRepository.AddBook(book);
if (success)
Console.WriteLine($"Book added successfully with ID: {book.ID}");
else
Console.WriteLine("Failed to add the book.");To update a book, create a new book instance with the updated properties and use the original book's Update method to apply the changes. Then, call the UpdateBook method to save the changes in the database:
// Create a new book instance with updated properties
IBook updatedBook = BookFactory.CreateBook("The Greater Adventure", book.ISBN, book.Genre, book.PublicationYear, book.BookAuthors, 15).Item;
// Apply updates to the original book
book.Update(updatedBook);
// Save changes in the database
bool updated = bookRepository.UpdateBook(book);
if (updated)
Console.WriteLine("Book updated successfully.");
else
Console.WriteLine("Failed to update the book.");To remove a book, call the RemoveBook method with the book object:
bool removed = bookRepository.RemoveBook(book);
if (removed)
Console.WriteLine("Book removed successfully.");
else
Console.WriteLine("Failed to remove the book.");To filter books based on criteria, use the FilterBooks method with optional parameters:
var filteredBooks = bookRepository.FilterBooks(authorName: "John", genre: "Fiction");
foreach (var filteredBook in filteredBooks)
{
Console.WriteLine($"Title: {filteredBook.Title}, Genre: {filteredBook.Genre}");
}You can also filter using a predicate for custom logic:
var customFilteredBooks = bookRepository.FilterBooks(book => book.PublicationYear > 2020);
foreach (var filteredBook in customFilteredBooks)
{
Console.WriteLine($"Title: {filteredBook.Title}, Published: {filteredBook.PublicationYear}");
}To retrieve a book by its ISBN, call the FindByISBN method:
var bookByISBN = bookRepository.FindByISBN("978-3-16-148410-0");
if (bookByISBN != null)
Console.WriteLine($"Found Book: {bookByISBN.Title}, Author(s): {string.Join(", ", bookByISBN.BookAuthors.Select(a => a.Name))}");
else
Console.WriteLine("No book found with the given ISBN.");These instructions demonstrate how to interact with the system's core functionalities in a straightforward manner. For any errors or issues encountered, refer to the logs generated by the centralized error logging mechanism for debugging purposes.
For any questions, feedback, or contributions, please reach out to:
- Name: Phiwokwakhe Khathwane
- Email: phiwokwakhe299@gmail.com
- GitHub: Phiwokwakhe Khathwane
- LinkedIn: Phiwokwakhe Khathwane
Feel free to open issues or pull requests on the GitHub repository for collaboration or improvements!

