Skip to content

Commit 2315a91

Browse files
committed
feat: introduce shared DTOs and global usings across projects
1 parent 49fbdd9 commit 2315a91

34 files changed

Lines changed: 138 additions & 436 deletions

BookStore.slnx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,16 @@
1313
<Project Path="src/BookStore.AppHost/BookStore.AppHost.csproj" />
1414
<Project Path="src/BookStore.ServiceDefaults/BookStore.ServiceDefaults.csproj" />
1515
</Folder>
16+
<Folder Name="/src/ApiService/">
17+
<Project Path="src/ApiService/BookStore.ApiService.Analyzers/BookStore.ApiService.Analyzers.csproj" />
18+
<Project Path="src/ApiService/BookStore.ApiService.Tests/BookStore.ApiService.Tests.csproj" />
19+
<Project Path="src/ApiService/BookStore.ApiService/BookStore.ApiService.csproj" />
20+
</Folder>
21+
<Folder Name="/src/Shared/">
22+
<Project Path="src/Shared/BookStore.Shared.Tests/BookStore.Shared.Tests.csproj" />
23+
<Project Path="src/Shared/BookStore.Shared/BookStore.Shared.csproj" />
24+
</Folder>
25+
<Folder Name="/src/Web/">
26+
<Project Path="src/Web/BookStore.Web/BookStore.Web.csproj" />
27+
</Folder>
1628
</Solution>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using BookStore.Shared.Models;

src/ApiService/BookStore.ApiService/BookStore.ApiService.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
<ItemGroup>
3939
<ProjectReference Include="..\BookStore.ApiService.Analyzers\BookStore.ApiService.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
40+
<ProjectReference Include="..\..\Shared\BookStore.Shared\BookStore.Shared.csproj" />
4041
</ItemGroup>
4142

4243
</Project>

src/ApiService/BookStore.ApiService/Endpoints/BookEndpoints.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static RouteGroupBuilder MapBookEndpoints(this RouteGroupBuilder group)
2929
return group;
3030
}
3131

32-
static async Task<Ok<PagedListDto<Models.BookDto>>> SearchBooks(
32+
static async Task<Ok<PagedListDto<BookDto>>> SearchBooks(
3333
[FromServices] IQuerySession session,
3434
[FromServices] IOptions<PaginationOptions> paginationOptions,
3535
[AsParameters] PagedRequest request,
@@ -79,7 +79,7 @@ public static RouteGroupBuilder MapBookEndpoints(this RouteGroupBuilder group)
7979
}
8080

8181
// Map to DTOs with localized categories, descriptions, and biographies
82-
var bookDtos = pagedList.Select(book => new Models.BookDto(
82+
var bookDtos = pagedList.Select(book => new BookDto(
8383
book.Id,
8484
book.Title,
8585
book.Isbn,
@@ -89,23 +89,23 @@ public static RouteGroupBuilder MapBookEndpoints(this RouteGroupBuilder group)
8989
book.PublicationDate,
9090
Helpers.BookHelpers.IsPreRelease(book.PublicationDate),
9191
book.PublisherId.HasValue && publishers.TryGetValue(book.PublisherId.Value, out var pub)
92-
? new Models.PublisherDto(pub.Id, pub.Name)
92+
? new PublisherDto(pub.Id, pub.Name)
9393
: null,
9494
[.. book.AuthorIds
9595
.Select(id => authors.TryGetValue(id, out var author)
96-
? new Models.AuthorDto(author.Id, author.Name, LocalizeBiography(author, language))
96+
? new AuthorDto(author.Id, author.Name, LocalizeBiography(author, language))
9797
: null)
9898
.Where(a => a != null)
99-
.Cast<Models.AuthorDto>()],
99+
.Cast<AuthorDto>()],
100100
[.. book.CategoryIds
101101
.Select(id => categories.TryGetValue(id, out var cat)
102102
? LocalizeCategory(cat, language)
103103
: null)
104104
.Where(c => c != null)
105-
.Cast<Models.CategoryDto>()]
105+
.Cast<CategoryDto>()]
106106
)).ToList();
107107

108-
return TypedResults.Ok(new PagedListDto<Models.BookDto>(
108+
return TypedResults.Ok(new PagedListDto<BookDto>(
109109
bookDtos,
110110
pagedList.PageNumber,
111111
pagedList.PageSize,
@@ -156,7 +156,7 @@ static async Task<IResult> GetBook(
156156
}
157157

158158
// Map to DTO with localized categories, description, and biographies
159-
var bookDto = new Models.BookDto(
159+
var bookDto = new BookDto(
160160
book.Id,
161161
book.Title,
162162
book.Isbn,
@@ -166,31 +166,31 @@ static async Task<IResult> GetBook(
166166
book.PublicationDate,
167167
Helpers.BookHelpers.IsPreRelease(book.PublicationDate),
168168
book.PublisherId.HasValue && publishers.TryGetValue(book.PublisherId.Value, out var pub)
169-
? new Models.PublisherDto(pub.Id, pub.Name)
169+
? new PublisherDto(pub.Id, pub.Name)
170170
: null,
171171
[.. book.AuthorIds
172172
.Select(id => authors.TryGetValue(id, out var author)
173-
? new Models.AuthorDto(author.Id, author.Name, LocalizeBiography(author, language))
173+
? new AuthorDto(author.Id, author.Name, LocalizeBiography(author, language))
174174
: null)
175175
.Where(a => a != null)
176-
.Cast<Models.AuthorDto>()],
176+
.Cast<AuthorDto>()],
177177
[.. book.CategoryIds
178178
.Select(catId => categories.TryGetValue(catId, out var cat)
179179
? LocalizeCategory(cat, language)
180180
: null)
181181
.Where(c => c != null)
182-
.Cast<Models.CategoryDto>()]);
182+
.Cast<CategoryDto>()]);
183183

184184
return TypedResults.Ok(bookDto);
185185
}
186186

187187
// Helper method for category localization with fallback strategy
188-
static Models.CategoryDto LocalizeCategory(CategoryProjection category, string language)
188+
static CategoryDto LocalizeCategory(CategoryProjection category, string language)
189189
{
190190
// Try full culture code first (e.g., "pt-PT")
191191
if (category.Translations.TryGetValue(language, out var localized))
192192
{
193-
return new Models.CategoryDto(category.Id, localized.Name);
193+
return new CategoryDto(category.Id, localized.Name);
194194
}
195195

196196
// Fallback to two-letter ISO language code (e.g., "pt" from "pt-PT")
@@ -201,7 +201,7 @@ static Models.CategoryDto LocalizeCategory(CategoryProjection category, string l
201201

202202
if (category.Translations.TryGetValue(twoLetterCode, out var twoLetterLocalized))
203203
{
204-
return new Models.CategoryDto(category.Id, twoLetterLocalized.Name);
204+
return new CategoryDto(category.Id, twoLetterLocalized.Name);
205205
}
206206
}
207207
catch (System.Globalization.CultureNotFoundException)
@@ -212,12 +212,12 @@ static Models.CategoryDto LocalizeCategory(CategoryProjection category, string l
212212
// Fallback to English
213213
if (category.Translations.TryGetValue("en", out var englishName))
214214
{
215-
return new Models.CategoryDto(category.Id, englishName.Name);
215+
return new CategoryDto(category.Id, englishName.Name);
216216
}
217217

218218
// Last resort: use first available localization
219219
var firstName = category.Translations.Values.FirstOrDefault();
220-
return new Models.CategoryDto(category.Id, firstName?.Name ?? "Unknown");
220+
return new CategoryDto(category.Id, firstName?.Name ?? "Unknown");
221221
}
222222

223223
// Helper method for book description localization with fallback strategy
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using BookStore.Shared.Models;

src/ApiService/BookStore.ApiService/Infrastructure/Extensions/JsonConfigurationExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using BookStore.ApiService.Infrastructure.Json;
1+
using BookStore.Shared.Infrastructure.Json;
22

33
namespace BookStore.ApiService.Infrastructure.Extensions;
44

src/ApiService/BookStore.ApiService/Infrastructure/Extensions/MartenConfigurationExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Marten.Events.Projections;
77
using Weasel.Core;
88
using Wolverine.Marten;
9+
using BookStore.Shared.Infrastructure.Json;
910

1011
namespace BookStore.ApiService.Infrastructure.Extensions;
1112

@@ -60,7 +61,7 @@ static void ConfigureJsonSerialization(StoreOptions options)
6061
Casing.CamelCase,
6162
configure: settings =>
6263
// Add custom converter for PartialDate to handle nullable values properly
63-
settings.Converters.Add(new Json.PartialDateJsonConverter()));
64+
settings.Converters.Add(new PartialDateJsonConverter()));
6465

6566
// Enable NGram search with unaccent for multilingual text search
6667
// This automatically enables pg_trgm and unaccent extensions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using BookStore.Shared.Models;
2+
using Marten.Pagination;
3+
4+
namespace BookStore.ApiService.Infrastructure.Extensions;
5+
6+
public static class PagedListExtensions
7+
{
8+
/// <summary>
9+
/// Creates a PagedListDto from Marten's IPagedList
10+
/// </summary>
11+
public static PagedListDto<T> ToPagedListDto<T>(this IPagedList<T> pagedList) => new()
12+
{
13+
Items = [.. pagedList],
14+
PageNumber = pagedList.PageNumber,
15+
PageSize = pagedList.PageSize,
16+
TotalItemCount = pagedList.TotalItemCount,
17+
PageCount = pagedList.PageCount,
18+
HasPreviousPage = pagedList.HasPreviousPage,
19+
HasNextPage = pagedList.HasNextPage
20+
};
21+
}

src/ApiService/BookStore.ApiService/Models/BookDto.cs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/ApiService/BookStore.ApiService/Models/PartialDate.cs

Lines changed: 0 additions & 139 deletions
This file was deleted.

0 commit comments

Comments
 (0)