-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: update book with new core logic
- Loading branch information
Showing
42 changed files
with
790 additions
and
448 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { ulid } from 'ulid' | ||
|
||
import CreateBookUseCase from '@/core/book/application/create-book.use-case' | ||
import { CreateBookCommand } from '@/core/book/application/types' | ||
import BookIdAlreadyExistsError from '@/core/book/domain/errors/book-id-already-exists.error' | ||
import Book from '@/core/book/domain/model/book.entity' | ||
import BooksInMemory from '@/core/book/infrastructure/services/books-in-memory.repository' | ||
import unexpected from '@/lib/utils/unexpected' | ||
|
||
describe('CreateBookUseCase', () => { | ||
it('should create a new book', async () => { | ||
// Arrange | ||
const books = new BooksInMemory() | ||
const book = Book.create({ | ||
authors: ['Jane Doe'], | ||
id: ulid(), | ||
image: 'http://example.com/book.jpeg', | ||
title: 'A book', | ||
})._unsafeUnwrap() | ||
|
||
const command = CreateBookCommand.with({ | ||
authors: book.authors.map((author) => author.value), | ||
id: book.id.value, | ||
image: book.image.value, | ||
title: book.title.value, | ||
}) | ||
const useCase = new CreateBookUseCase(books) | ||
|
||
// Act | ||
const result = await useCase.with(command) | ||
|
||
// Assert | ||
result.match( | ||
(_book) => { | ||
const savedBook = books.books.get(_book.id.value) | ||
expect(savedBook).toEqual(_book) | ||
}, | ||
(error) => unexpected.error(error), | ||
) | ||
}) | ||
|
||
it('should rejects to create a book with the same id', async () => { | ||
// Arrange | ||
const books = new BooksInMemory() | ||
const book = Book.create({ | ||
authors: ['Jane Doe'], | ||
id: ulid(), | ||
image: 'http://example.com/book.jpeg', | ||
title: 'A book', | ||
})._unsafeUnwrap() | ||
books.books.set(book.id.value, book) | ||
|
||
const command = CreateBookCommand.with({ | ||
authors: book.authors.map((author) => author.value), | ||
id: book.id.value, | ||
image: book.image.value, | ||
title: book.title.value, | ||
}) | ||
const useCase = new CreateBookUseCase(books) | ||
|
||
// Act | ||
const result = await useCase.with(command) | ||
|
||
// Assert | ||
result.match( | ||
(success) => unexpected.success(success), | ||
(error) => { | ||
expect(error).toBeInstanceOf(BookIdAlreadyExistsError) | ||
}, | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,22 @@ | ||
import { err, ok, Result } from 'neverthrow' | ||
import { errAsync } from 'neverthrow' | ||
|
||
import Book from '../domain/model/book.entity' | ||
import BookId from '../domain/model/id.value-object' | ||
import Books from '../domain/services/books.repository' | ||
import { BookError, CreateBookCommand } from './types' | ||
import BookIdAlreadyExistsError from '@/core/book/domain/errors/book-id-already-exists.error' | ||
import Book from '@/core/book/domain/model/book.entity' | ||
import Books from '@/core/book/domain/services/books.repository' | ||
import BookId from '@/core/common/domain/value-objects/book-id' | ||
|
||
export default class CreateBookUseCase { | ||
constructor(private readonly bookRepository: Books) {} | ||
|
||
async with(command: CreateBookCommand): Promise<Result<true, BookError>> { | ||
const bookId = BookId.create(command.id) | ||
|
||
if (await this.bookRepository.findById(bookId)) { | ||
return err(BookError.becauseAlreadyExists(bookId)) | ||
} | ||
import { CreateBookCommand } from './types' | ||
|
||
const book = Book.create( | ||
command.id, | ||
command.authors, | ||
command.title, | ||
command.image, | ||
) | ||
|
||
await this.bookRepository.save(book) | ||
export default class CreateBookUseCase { | ||
constructor(private readonly books: Books) {} | ||
|
||
return ok(true) | ||
async with(command: CreateBookCommand) { | ||
return await BookId.create(command.id) | ||
.asyncAndThen((bookId) => this.books.findById(bookId)) | ||
.match( | ||
(book) => errAsync(BookIdAlreadyExistsError.withId(book.id)), | ||
() => | ||
Book.create(command).asyncAndThen((_book) => this.books.save(_book)), | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { ulid } from 'ulid' | ||
|
||
import FindBooksUseCase from '@/core/book/application/find-books.use-case' | ||
import { BookDTO } from '@/core/book/application/types' | ||
import Book from '@/core/book/domain/model/book.entity' | ||
import BooksInMemory from '@/core/book/infrastructure/services/books-in-memory.repository' | ||
import unexpected from '@/lib/utils/unexpected' | ||
|
||
describe('FindBooksUseCase', () => { | ||
it('should get all books', async () => { | ||
// Arrange | ||
const books = new BooksInMemory() | ||
const book = Book.create({ | ||
authors: ['Jane Doe'], | ||
id: ulid(), | ||
image: 'http://example.com/book.jpeg', | ||
title: 'A book', | ||
})._unsafeUnwrap() | ||
books.books.set(book.id.value, book) | ||
|
||
const useCase = new FindBooksUseCase(books) | ||
|
||
// Act | ||
const result = await useCase.with() | ||
|
||
// Assert | ||
result.match( | ||
(_books) => { | ||
expect(_books).toEqual([BookDTO.fromModel(book)]) | ||
}, | ||
(error) => unexpected.error(error), | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,15 @@ | ||
import Books from '@/core/book/domain/services/books.repository' | ||
import { okAsync, ResultAsync } from 'neverthrow' | ||
|
||
import { BookDTO } from './types' | ||
import { BookDTO } from '@/core/book/application/types' | ||
import Books from '@/core/book/domain/services/books.repository' | ||
import ApplicationError from '@/core/common/domain/errors/application-error' | ||
|
||
export default class FindBooksUseCase { | ||
constructor(private readonly booksRepository: Books) {} | ||
|
||
async with(): Promise<BookDTO[]> { | ||
const books = await this.booksRepository.findAll() | ||
constructor(private readonly books: Books) {} | ||
|
||
return books.map((book) => ({ | ||
authors: book.authors, | ||
id: book.id, | ||
image: book.image, | ||
title: book.title, | ||
})) | ||
with(): ResultAsync<BookDTO[], ApplicationError> { | ||
return this.books.findAll().andThen((books) => { | ||
return okAsync(books.map((book) => BookDTO.fromModel(book))) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,33 @@ | ||
import BookId from '@/core/book/domain/model/id.value-object' | ||
import { DeepReadonly } from 'ts-essentials' | ||
|
||
export class CreateBookCommand { | ||
constructor( | ||
public readonly id: string, | ||
public readonly title: string, | ||
public readonly authors: string[], | ||
public readonly image: string, | ||
) {} | ||
} | ||
import Book from '@/core/book/domain/model/book.entity' | ||
|
||
export interface BookDTO { | ||
type CreateBookCommand = DeepReadonly<{ | ||
authors: string[] | ||
id: string | ||
image: string | ||
title: string | ||
}> | ||
|
||
const CreateBookCommand = { | ||
with: (properties: CreateBookCommand) => properties, | ||
} | ||
|
||
export class BookError extends Error { | ||
constructor( | ||
message: string, | ||
public readonly type: string, | ||
) { | ||
super(message) | ||
} | ||
type BookDTO = DeepReadonly<{ | ||
authors: string[] | ||
id: string | ||
image: string | ||
title: string | ||
}> | ||
|
||
static becauseAlreadyExists(id: BookId) { | ||
return new BookError( | ||
`Book with id ${id.value} already exists`, | ||
'DUPLICATE_NAME', | ||
) | ||
} | ||
const BookDTO = { | ||
fromModel: (book: Book): BookDTO => ({ | ||
authors: book.authors.map((author) => author.value), | ||
id: book.id.value, | ||
image: book.image.value, | ||
title: book.title.value, | ||
}), | ||
with: (properties: BookDTO) => properties, | ||
} | ||
|
||
export { BookDTO, CreateBookCommand } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import ApplicationError from '@/core/common/domain/errors/application-error' | ||
import BookId from '@/core/common/domain/value-objects/book-id' | ||
|
||
export default class BookIdAlreadyExistsError extends ApplicationError { | ||
static withId(id: BookId): BookIdAlreadyExistsError { | ||
return new BookIdAlreadyExistsError(`book with ${id.value} already exists.`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import ApplicationError from '@/core/common/domain/errors/application-error' | ||
import BookId from '@/core/common/domain/value-objects/book-id' | ||
|
||
export default class BookNotFoundError extends ApplicationError { | ||
static withId(id: BookId): BookNotFoundError { | ||
return new BookNotFoundError(`book with ${id.value} not found`) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.