A simple ASP.NET Core Web API for managing a collection of books.
-
Prerequisites:
- .NET 9.0 SDK
- A code editor like Visual Studio Code or Visual Studio.
-
Clone the repository:
git clone <your-repository-url> cd BooksApp
-
Run the application: Open a terminal in the project's root directory and run the following command:
dotnet run
The application will start, and you can access it at
http://localhost:5196.
Here's a breakdown of the important files in the project:
-
Program.cs: The entry point of the application. It sets up the web server, configures dependency injection, and defines the middleware pipeline. It also registers theBookContextto use an in-memory database. -
Controllers/BookController.cs: This file contains the API controller for handling book-related requests. It defines the API endpoints for creating, reading, updating, and deleting books (CRUD operations). -
Services/BookService.cs: This file contains the business logic for managing books. TheBookServiceclass implements theIBookServiceinterface and interacts with theBookContextto perform data operations. -
Data/BookContext.cs: This is the Entity Framework CoreDbContextclass. It defines the database schema and is configured to use an in-memory database for storing book data. It also seeds the database with some initial data. -
Models/Book.cs: This file defines theBookdata model, which represents a book with properties likeId,Title,Author, andYearPublished. -
BooksApp.csproj: The project file, which defines project settings, dependencies (like Entity Framework Core InMemory), and other configurations.
You can use a tool like Postman to test the API endpoints. Here's how to test each endpoint:
- Method:
GET - URL:
http://localhost:5196/api/books - Description: Retrieves a list of all books.
- Method:
GET - URL:
http://localhost:5196/api/books/{id}(e.g.,http://localhost:5196/api/books/1) - Description: Retrieves a single book by its ID.
- Method:
POST - URL:
http://localhost:5196/api/books - Body:
- Select
rawandJSONformat. - Example:
{ "title": "The Hobbit", "author": "J.R.R. Tolkien", "yearPublished": 1937 }
- Select
- Description: Adds a new book to the collection.
- Method:
PUT - URL:
http://localhost:5196/api/books/{id}(e.g.,http://localhost:5196/api/books/1) - Body:
- Select
rawandJSONformat. - Example:
{ "id": 1, "title": "The Lord of the Rings: The Fellowship of the Ring", "author": "J.R.R. Tolkien", "yearPublished": 1954 }
- Select
- Description: Updates an existing book.
- Method:
DELETE - URL:
http://localhost:5196/api/books/{id}(e.g.,http://localhost:5196/api/books/1) - Description: Deletes a book by its ID.
Here's a breakdown of some of the key architectural decisions made in this project and the reasoning behind them.
I opted to use Entity Framework Core's DbContext with an in-memory provider for a few practical reasons:
-
Paving the Way for a Real Database: When it's time to get serious, switching to something like SQL Server or PostgreSQL is as simple as swapping out one line of code in the service configuration. The rest of the application, especially the business logic in
BookService, won't need to change at all. -
Leveraging EF Core's Power: I'm letting EF Core do the heavy lifting. It gives me powerful tools for querying data with LINQ, handling data transactions, and managing the database schema.
-
Clear Separation of Concerns: The
BookContextacts as a clean boundary between the application's business logic and the data storage mechanism. TheBookServicedoesn't need to know how the data is stored, just that it can get the data from the context. This makes the code cleaner and easier to reason about.
-
Testable Code: Take a look at the
BookController. It doesn't create anew BookService(). Instead, it asks for anIBookServicein its constructor. This is huge for testing. When I'm writing unit tests for the controller, I can easily give it a "mock" version of theIBookServicethat returns predictable data. This way, I can test the controller's logic in isolation without having to worry about the database. -
Centralized Service Management: All of the application's services are registered in one place:
Program.cs. This makes it easy to see how the different parts of the application fit together and to manage their lifecycles (e.g., should this service be a singleton, or should a new one be created for each request?).