diff --git a/LibraryManagement.Api.Tests.Unit/DeleteMe.cs b/LibraryManagement.Api.Tests.Unit/DeleteMe.cs
deleted file mode 100644
index 8f56efd..0000000
--- a/LibraryManagement.Api.Tests.Unit/DeleteMe.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace LibraryManagement.Api.Tests.Unit
-{
- public class DeleteMe
- {
- [Fact]
- public void ShouldBeTrue() => Assert.True(true);
- }
-}
diff --git a/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj b/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj
index 9c5b30a..dc41089 100644
--- a/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj
+++ b/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj
@@ -10,10 +10,24 @@
-
-
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs
new file mode 100644
index 0000000..54e1e5c
--- /dev/null
+++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs
@@ -0,0 +1,133 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using EFxceptions.Models.Exceptions;
+using LibraryManagement.Api.Models.Foundations.Books;
+using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
+using Microsoft.Data.SqlClient;
+using Moq;
+
+namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
+{
+ public partial class BookServiceTests
+ {
+ [Fact]
+ public async Task ShouldThrowCriticalDependencyExceptionOnAddIfSqlErrorOccursAndLogItAsync()
+ {
+ // given
+ Book someBook = CreateRandomBook();
+ SqlException sqlException = GetSqlError();
+
+ var failedBookStorageException =
+ new FailedBookStorageException(sqlException);
+
+ var expectedBookDependencyException =
+ new BookDependencyException(failedBookStorageException);
+
+ this.storageBrokerMock.Setup(broker =>
+ broker.InsertBookAsync(someBook))
+ .ThrowsAsync(sqlException);
+
+ // when
+ ValueTask addBookTask =
+ this.bookService.AddBookAsync(someBook);
+
+ // then
+ await Assert.ThrowsAsync(() =>
+ addBookTask.AsTask());
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(someBook),
+ Times.Once);
+
+ this.loggingBrokerMock.Verify(broker =>
+ broker.LogCritical(It.Is(SameExceptionAs(
+ expectedBookDependencyException))),
+ Times.Once);
+
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task ShouldThrowDependencyValidationOnAddIfDuplicateKeyErrorOccursAndLogItAsync()
+ {
+ // given
+ Book someBook = CreateRandomBook();
+ string someMessage = GetRandomString();
+
+ var duplicateKeyException =
+ new DuplicateKeyException(someMessage);
+
+ var alreadyExistsBookException =
+ new AlreadyExistsBookException(duplicateKeyException);
+
+ var expectedBookDependencyValidationException =
+ new BookDependencyValidationException(alreadyExistsBookException);
+
+ this.storageBrokerMock.Setup(broker =>
+ broker.InsertBookAsync(someBook))
+ .ThrowsAsync(duplicateKeyException);
+
+ // when
+ ValueTask addBookTask =
+ this.bookService.AddBookAsync(someBook);
+
+ // then
+ await Assert.ThrowsAsync(() =>
+ addBookTask.AsTask());
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(someBook),
+ Times.Once);
+
+ this.loggingBrokerMock.Verify(broker =>
+ broker.LogError(It.Is(SameExceptionAs(
+ expectedBookDependencyValidationException))),
+ Times.Once);
+
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task ShouldThrowServiceExceptionOnAddIfServiceErrorOccursAndLogItAsync()
+ {
+ // given
+ Book someBook = CreateRandomBook();
+ var serviceException = new Exception();
+
+ var failedBookServiceException =
+ new FailedBookServiceException(serviceException);
+
+ var expectedBookServiceException =
+ new BookServiceException(failedBookServiceException);
+
+ this.storageBrokerMock.Setup(broker =>
+ broker.InsertBookAsync(someBook))
+ .ThrowsAsync(serviceException);
+
+ // when
+ ValueTask addBookTask =
+ this.bookService.AddBookAsync(someBook);
+
+ // then
+ await Assert.ThrowsAsync(() =>
+ addBookTask.AsTask());
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(someBook),
+ Times.Once);
+
+ this.loggingBrokerMock.Verify(broker =>
+ broker.LogError(It.Is(SameExceptionAs(
+ expectedBookServiceException))),
+ Times.Once);
+
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ }
+ }
+}
diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs
new file mode 100644
index 0000000..000a25b
--- /dev/null
+++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using FluentAssertions;
+using Force.DeepCloner;
+using LibraryManagement.Api.Models.Foundations.Books;
+using Moq;
+
+namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
+{
+ public partial class BookServiceTests
+ {
+ [Fact]
+ public async Task ShouldAddBookAsync()
+ {
+ // given
+ Book randomBook = CreateRandomBook();
+ Book inputBook = randomBook;
+ Book storageBook = inputBook;
+ Book expectedBook = storageBook.DeepClone();
+
+ this.storageBrokerMock.Setup(broker =>
+ broker.InsertBookAsync(inputBook))
+ .ReturnsAsync(storageBook);
+
+ // when
+ Book actualBook =
+ await this.bookService.AddBookAsync(inputBook);
+
+ // then
+ actualBook.Should().BeEquivalentTo(expectedBook);
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(inputBook),
+ Times.Once);
+
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ }
+ }
+}
diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs
new file mode 100644
index 0000000..0844067
--- /dev/null
+++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs
@@ -0,0 +1,104 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using LibraryManagement.Api.Models.Foundations.Books;
+using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
+using Moq;
+
+namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
+{
+ public partial class BookServiceTests
+ {
+ [Fact]
+ public async Task ShouldThrowValidationExceptionOnAddIfBookIsNullAndLogItAsync()
+ {
+ // given
+ Book nullBook = null;
+ var nullBookException = new NullBookException();
+
+ var expectedBookValidationException =
+ new BookValidationException(nullBookException);
+
+ // when
+ ValueTask addBookTask =
+ this.bookService.AddBookAsync(nullBook);
+
+ // then
+ await Assert.ThrowsAsync(() =>
+ addBookTask.AsTask());
+
+ this.loggingBrokerMock.Verify(broker =>
+ broker.LogError(It.Is(SameExceptionAs(
+ expectedBookValidationException))),
+ Times.Once);
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(It.IsAny()),
+ Times.Never);
+
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public async Task ShouldThrowValidationExceptionOnAddIfBookIsInvalidAndLogItAsync(
+ string invalidText)
+ {
+ var invalidBook = new Book
+ {
+ BookTitle = invalidText
+ };
+
+ var invalidBookException = new InvalidBookException();
+
+ invalidBookException.AddData(
+ key: nameof(Book.BookId),
+ values: "Id is required");
+
+ invalidBookException.AddData(
+ key: nameof(Book.ReaderId),
+ values: "Id is required");
+
+ invalidBookException.AddData(
+ key: nameof(Book.BookTitle),
+ values: "Text is required");
+
+ invalidBookException.AddData(
+ key: nameof(Book.Author),
+ values: "Text is required");
+
+ invalidBookException.AddData(
+ key: nameof(Book.Genre),
+ values: "Text is required");
+
+
+ var expectedBookValidationException =
+ new BookValidationException(invalidBookException);
+
+ // when
+ ValueTask addBookTask =
+ this.bookService.AddBookAsync(invalidBook);
+
+ // then
+ await Assert.ThrowsAsync(() =>
+ addBookTask.AsTask());
+
+ this.loggingBrokerMock.Verify(broker =>
+ broker.LogError(It.Is(SameExceptionAs(
+ expectedBookValidationException))),
+ Times.Once);
+
+ this.storageBrokerMock.Verify(broker =>
+ broker.InsertBookAsync(It.IsAny()),
+ Times.Never);
+
+ this.loggingBrokerMock.VerifyNoOtherCalls();
+ this.storageBrokerMock.VerifyNoOtherCalls();
+ }
+ }
+}
diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs
new file mode 100644
index 0000000..ea1bcb9
--- /dev/null
+++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs
@@ -0,0 +1,59 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using LibraryManagement.Api.Brokers.Loggings;
+using LibraryManagement.Api.Brokers.Storages;
+using LibraryManagement.Api.Models.Foundations.Books;
+using LibraryManagement.Api.Services.Foundations.Books;
+using Microsoft.Data.SqlClient;
+using Moq;
+using Tynamix.ObjectFiller;
+using Xeptions;
+
+namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
+{
+ public partial class BookServiceTests
+ {
+ private readonly Mock storageBrokerMock;
+ private readonly Mock loggingBrokerMock;
+ private readonly IBookService bookService;
+
+ public BookServiceTests()
+ {
+ this.storageBrokerMock = new Mock();
+ this.loggingBrokerMock = new Mock();
+ this.bookService = new BookService(
+ storageBroker: this.storageBrokerMock.Object,
+ loggingBroker: this.loggingBrokerMock.Object);
+ }
+
+ private static Book CreateRandomBook() =>
+ CreateBookFiller(date: GetRandomDateTimeOffset()).Create();
+
+ private static DateTimeOffset GetRandomDateTimeOffset() =>
+ new DateTimeRange(earliestDate: new DateTime()).GetValue();
+
+ private static SqlException GetSqlError() =>
+ (SqlException)RuntimeHelpers.GetUninitializedObject(typeof(SqlException));
+
+ private static string GetRandomString() =>
+ new MnemonicString().GetValue();
+
+ private Expression> SameExceptionAs(Xeption expectedException) =>
+ actualException => actualException.SameExceptionAs(expectedException);
+
+ private static Filler CreateBookFiller(DateTimeOffset date)
+ {
+ var filler = new Filler();
+
+ filler.Setup()
+ .OnType().Use(date);
+
+ return filler;
+ }
+ }
+}
diff --git a/LibraryManagement.Api/LibraryManagement.Api.csproj b/LibraryManagement.Api/LibraryManagement.Api.csproj
index fcfae9d..08bc71b 100644
--- a/LibraryManagement.Api/LibraryManagement.Api.csproj
+++ b/LibraryManagement.Api/LibraryManagement.Api.csproj
@@ -19,6 +19,7 @@
+
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs
new file mode 100644
index 0000000..606917a
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs
@@ -0,0 +1,16 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class AlreadyExistsBookException : Xeption
+ {
+ public AlreadyExistsBookException(Exception innerException)
+ : base(message: "Book already exists.", innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs
new file mode 100644
index 0000000..57ad8b2
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class BookDependencyException : Xeption
+ {
+ public BookDependencyException(Xeption innerException)
+ : base(message: "Book dependency error occurred, contact support.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs
new file mode 100644
index 0000000..ab12175
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class BookDependencyValidationException : Xeption
+ {
+ public BookDependencyValidationException(Xeption innerException)
+ : base(message: "Book dependency validation errors occurred, fix the errors and try again.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs
new file mode 100644
index 0000000..1e7ed2c
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class BookServiceException : Xeption
+ {
+ public BookServiceException(Xeption innerException)
+ : base(message: "Book service error occurred, contact support.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs
new file mode 100644
index 0000000..bdc8ed1
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class BookValidationException : Xeption
+ {
+ public BookValidationException(Xeption innerException)
+ : base(message: "Book validation errors occurred, fix the errors and try again.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs
new file mode 100644
index 0000000..fac8434
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class FailedBookServiceException : Xeption
+ {
+ public FailedBookServiceException(Exception innerException)
+ : base(message: "Failed book service error occurred, contact support.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs
new file mode 100644
index 0000000..1463b8f
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs
@@ -0,0 +1,17 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class FailedBookStorageException : Xeption
+ {
+ public FailedBookStorageException(Exception innerException)
+ : base(message: "Failed book storage error occurred, contact support.",
+ innerException)
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs
new file mode 100644
index 0000000..8ef0135
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs
@@ -0,0 +1,16 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class InvalidBookException : Xeption
+ {
+ public InvalidBookException()
+ : base(message: "Book is invalid.")
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs
new file mode 100644
index 0000000..f6d8b4f
--- /dev/null
+++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs
@@ -0,0 +1,16 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using Xeptions;
+
+namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions
+{
+ public class NullBookException : Xeption
+ {
+ public NullBookException()
+ : base(message: "Book is null.")
+ { }
+ }
+}
diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs
new file mode 100644
index 0000000..ffa4d07
--- /dev/null
+++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs
@@ -0,0 +1,95 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using EFxceptions.Models.Exceptions;
+using LibraryManagement.Api.Models.Foundations.Books;
+using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
+using Microsoft.Data.SqlClient;
+using Xeptions;
+
+namespace LibraryManagement.Api.Services.Foundations.Books
+{
+ public partial class BookService
+ {
+ private delegate ValueTask ReturningBookFunction();
+
+ private async ValueTask TryCatch(ReturningBookFunction returningBookFunction)
+ {
+ try
+ {
+ return await returningBookFunction();
+ }
+ catch (NullBookException nullBookException)
+ {
+ throw CreateAndLogValidationException(nullBookException);
+ }
+ catch (InvalidBookException invalidBookException)
+ {
+ throw CreateAndLogValidationException(invalidBookException);
+ }
+ catch (SqlException sqlException)
+ {
+ var failedBookStorageException =
+ new FailedBookStorageException(sqlException);
+
+ throw CreateAndLogCriticalDependencyException(failedBookStorageException);
+ }
+ catch (DuplicateKeyException duplicateKeyException)
+ {
+ var alreadyExistsBookException =
+ new AlreadyExistsBookException(duplicateKeyException);
+
+ throw CreateAndLogDependencyValidationException(alreadyExistsBookException);
+ }
+ catch (Exception exception)
+ {
+ var failedBookServiceException =
+ new FailedBookServiceException(exception);
+
+ throw CreateAndLogServiceException(failedBookServiceException);
+ }
+ }
+
+ private BookValidationException CreateAndLogValidationException(Xeption exception)
+ {
+ var bookValidationException =
+ new BookValidationException(exception);
+
+ this.loggingBroker.LogError(bookValidationException);
+
+ return bookValidationException;
+ }
+
+ private BookDependencyException CreateAndLogCriticalDependencyException(Xeption exception)
+ {
+ var bookDependencyException =
+ new BookDependencyException(exception);
+
+ this.loggingBroker.LogCritical(bookDependencyException);
+
+ return bookDependencyException;
+ }
+
+ private BookDependencyValidationException CreateAndLogDependencyValidationException(Xeption exception)
+ {
+ var bookDependencyValidationException =
+ new BookDependencyValidationException(exception);
+
+ this.loggingBroker.LogError(bookDependencyValidationException);
+
+ return bookDependencyValidationException;
+ }
+
+ private BookServiceException CreateAndLogServiceException(Xeption exception)
+ {
+ var bookServiceException =
+ new BookServiceException(exception);
+
+ this.loggingBroker.LogError(bookServiceException);
+
+ return bookServiceException;
+ }
+ }
+}
diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs
new file mode 100644
index 0000000..be3a158
--- /dev/null
+++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using LibraryManagement.Api.Models.Foundations.Books;
+using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
+
+namespace LibraryManagement.Api.Services.Foundations.Books
+{
+ public partial class BookService
+ {
+ private void ValidateBookOnAdd(Book book)
+ {
+ ValidateBookNotNull(book);
+
+ Validate(
+ (Rule: IsInvalid(book.BookId), Parameter: nameof(Book.BookId)),
+ (Rule: IsInvalid(book.ReaderId), Parameter: nameof(Book.ReaderId)),
+ (Rule: IsInvalid(book.BookTitle), Parameter: nameof(Book.BookTitle)),
+ (Rule: IsInvalid(book.Author), Parameter: nameof(Book.Author)),
+ (Rule: IsInvalid(book.Genre), Parameter: nameof(Book.Genre)));
+ }
+ private void ValidateBookNotNull(Book book)
+ {
+ if (book is null)
+ {
+ throw new NullBookException();
+ }
+ }
+
+ private static dynamic IsInvalid(Guid id) => new
+ {
+ Condition = id == Guid.Empty,
+ Message = "Id is required"
+ };
+
+ private static dynamic IsInvalid(string text) => new
+ {
+ Condition = string.IsNullOrWhiteSpace(text),
+ Message = "Text is required"
+ };
+
+ private static void Validate(params (dynamic Rule, string Parameter)[] validations)
+ {
+ var invalidBookException = new InvalidBookException();
+
+ foreach ((dynamic rule, string parameter) in validations)
+ {
+ if (rule.Condition)
+ {
+ invalidBookException.UpsertDataList(
+ key: parameter,
+ value: rule.Message);
+ }
+ }
+
+ invalidBookException.ThrowIfContainsErrors();
+ }
+ }
+}
diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.cs
new file mode 100644
index 0000000..463db8c
--- /dev/null
+++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using LibraryManagement.Api.Brokers.Loggings;
+using LibraryManagement.Api.Brokers.Storages;
+using LibraryManagement.Api.Models.Foundations.Books;
+
+namespace LibraryManagement.Api.Services.Foundations.Books
+{
+ public partial class BookService : IBookService
+ {
+ private readonly IStorageBroker storageBroker;
+ private readonly ILoggingBroker loggingBroker;
+
+ public BookService(
+ IStorageBroker storageBroker,
+ ILoggingBroker loggingBroker)
+ {
+ this.storageBroker = storageBroker;
+ this.loggingBroker = loggingBroker;
+ }
+
+ public ValueTask AddBookAsync(Book book) =>
+ TryCatch(async () =>
+ {
+ ValidateBookOnAdd(book);
+
+ return await this.storageBroker.InsertBookAsync(book);
+ });
+ }
+}
diff --git a/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs b/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs
new file mode 100644
index 0000000..c6dea03
--- /dev/null
+++ b/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs
@@ -0,0 +1,14 @@
+//-----------------------------------------------------------
+// Copyright (c) Coalition of Good-Hearted Engineers
+// Free To Use To Build Reliable Library Management Solutions
+//-----------------------------------------------------------
+
+using LibraryManagement.Api.Models.Foundations.Books;
+
+namespace LibraryManagement.Api.Services.Foundations.Books
+{
+ public interface IBookService
+ {
+ ValueTask AddBookAsync(Book book);
+ }
+}