Nesta série de tutoriais, você criará um aplicativo usado para gerenciar uma lista de livros e seus autores. **Angular** será usado como estrutura da interface do usuário e **MongoDB** será usado como provedor de banco de dados.
Esta é a primeira parte da série de tutoriais angulares. Veja todas as peças:
- **Parte I: Crie o projeto e uma página de lista de livros (este tutorial)**
- [Parte II: Criar, atualizar e excluir livros](Part-II)
- [Parte III: Testes de Integração](Part-III)
Você pode acessar o **código fonte** do aplicativo no [repositório GitHub](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb) .
### Criando o projeto
Crie um novo projeto nomeado `Acme.BookStore`selecionando Angular como a estrutura da interface do usuário e MongoDB como o provedor de banco de dados, crie o banco de dados e execute o aplicativo seguindo o [documento Introdução](../../Getting-Started-Angular-Template.md) .
### Estrutura da solução (back-end)
É assim que a estrutura da solução em camadas cuida da criação:
![solução de back-end da livraria](images/bookstore-backend-solution-v2.png)
> Você pode ver o [documento do modelo de aplicativo](../../Startup-Templates/Application.md) para entender a estrutura da solução em detalhes. No entanto, você entenderá o básico com este tutorial.
### Criar a entidade do livro
A camada de domínio no modelo de inicialização é separada em dois projetos:
-`Acme.BookStore.Domain`contém suas [entidades](../../Entities.md) , [serviços de domínio](../../Domain-Services.md) e outros objetos principais de domínio.
-`Acme.BookStore.Domain.Shared` contém constantes, enumerações ou outros objetos relacionados ao domínio que podem ser compartilhados com os clientes.
Defina [entidades](../../Entities.md) na **camada de domínio** ( `Acme.BookStore.Domain`projeto) da solução. A entidade principal do aplicativo é a `Book`. Crie uma classe, chamada `Book`, no `Acme.BookStore.Domain`projeto, como mostrado abaixo:
```csharp
using System;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
```
- O ABP possui duas classes base fundamentais para entidades: `AggregateRoot`e `Entity`. **A raiz agregada** é um dos conceitos de **DDD (Domain Driven Design)** . Consulte o [documento da entidade](../../Entities.md) para obter detalhes e melhores práticas.
-`Book`entidade herda `AuditedAggregateRoot`que adiciona algumas propriedades de auditoria ( `CreationTime`, `CreatorId`, `LastModificationTime`... etc.) no topo da `AggregateRoot`classe.
-`Guid`é o **tipo** de **chave primária** da `Book`entidade.
#### BookType Enum
Defina a `BookType`enumeração no `Acme.BookStore.Domain.Shared`projeto:
```csharp
namespace Acme.BookStore
{
public enum BookType
{
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry
}
}
```
#### Adicionar entidade de livro ao seu DbContext
Adicione uma `IMongoCollection`propriedade ao `BookStoreMongoDbContext`interior do `Acme.BookStore.MongoDB`projeto:
```csharp
public class BookStoreMongoDbContext : AbpMongoDbContext
{
public IMongoCollection<Book> Books => Collection<Book>();
...
}
```
#### Adicionar dados de semente (amostra)
Esta seção é opcional, mas seria bom ter um dado inicial no banco de dados na primeira execução. O ABP fornece um [sistema de semente de dados](../../Data-Seeding.md) . Crie uma classe derivada de `IDataSeedContributor`no `.Domain`projeto:
public BookStoreDataSeederContributor(IRepository<Book,Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
}
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
}
);
}
}
}
```
`BookStoreDataSeederContributor`simplesmente insere dois livros no banco de dados se não houver nenhum livro adicionado antes. O ABP descobre e executa automaticamente essa classe quando você propaga o banco de dados executando o `Acme.BookStore.DbMigrator`projeto.
### Crie o serviço de aplicativo
O próximo passo é criar um [serviço de aplicativo](../../Application-Services.md) para gerenciar (criar, listar, atualizar, excluir ...) os livros. A camada de aplicativo no modelo de inicialização é separada em dois projetos:
-`Acme.BookStore.Application.Contracts` contém principalmente seus DTOs e interfaces de serviço de aplicativo.
-`Acme.BookStore.Application` contém as implementações dos seus serviços de aplicativo.
#### BookDto
Crie uma classe DTO denominada `BookDto`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore
{
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
```
- **As** classes **DTO** são usadas para **transferir dados** entre a *camada de apresentação* e a *camada de aplicativo* . Consulte o [documento Objetos de transferência de dados](../../Data-Transfer-Objects.md) para obter mais detalhes.
-`BookDto` é usado para transferir dados do livro para a camada de apresentação para mostrar as informações do livro na interface do usuário.
-`BookDto`é derivado do `AuditedEntityDto<Guid>`que possui propriedades de auditoria exatamente como a `Book`classe definida acima.
Será necessário converter `Book`entidades em `BookDto`objetos enquanto retorna os livros para a camada de apresentação. A biblioteca do [AutoMapper](https://automapper.org/) pode automatizar essa conversão quando você define o mapeamento adequado. O modelo de inicialização é fornecido com o AutoMapper configurado, para que você possa definir o mapeamento na `BookStoreApplicationAutoMapperProfile`classe no `Acme.BookStore.Application`projeto:
```csharp
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book,BookDto>();
}
}
}
```
#### CreateUpdateBookDto
Crie uma classe DTO denominada `CreateUpdateBookDto`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using System.ComponentModel.DataAnnotations;
namespace Acme.BookStore
{
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public DateTime PublishDate { get; set; }
[Required]
public float Price { get; set; }
}
}
```
- Essa classe DTO é usada para obter informações do livro a partir da interface do usuário ao criar ou atualizar um livro.
- Ele define atributos de anotação de dados (como `[Required]`) para definir validações para as propriedades. Os DTOs são [validados automaticamente](../../Validation.md) pela estrutura ABP.
Em seguida, adicione um mapeamento `BookStoreApplicationAutoMapperProfile`do `CreateUpdateBookDto`objeto à `Book`entidade:
```csharp
CreateMap<CreateUpdateBookDto,Book>();
```
#### IBookAppService
Defina uma interface nomeada `IBookAppService`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService :
ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
CreateUpdateBookDto, //Used to create a new book
CreateUpdateBookDto> //Used to update a book
{
}
}
```
- A definição de interfaces para serviços de aplicativos não é requerida pela estrutura. No entanto, é sugerido como uma prática recomendada.
-`ICrudAppService`define comuns **CRUD** métodos: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync`e `DeleteAsync`. Não é necessário estendê-lo. Em vez disso, você pode herdar da `IApplicationService`interface vazia e definir seus próprios métodos manualmente.
- Existem algumas variações de `ICrudAppService`onde você pode usar DTOs separados para cada método.
public BookAppService(IRepository<Book,Guid> repository)
: base(repository)
{
}
}
}
```
-`BookAppService`é derivado do `CrudAppService<...>`qual implementa todos os métodos CRUD definidos acima.
-`BookAppService`injeta `IRepository<Book, Guid>`qual é o repositório padrão da `Book`entidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o [documento do repositório](../../Repositories) .
-`BookAppService`usa `IObjectMapper`para converter `Book`objetos em `BookDto`objetos e `CreateUpdateBookDto`objetos em `Book`objetos. O modelo de inicialização usa a biblioteca [AutoMapper](http://automapper.org/) como o provedor de mapeamento de objetos. Você definiu os mapeamentos antes, para que funcionem conforme o esperado.
### Controladores de API automática
Você normalmente cria **controladores** para expor serviços de aplicativos como pontos de extremidade da **API HTTP** . Assim, permite que navegadores ou clientes de terceiros os chamem via AJAX. O ABP pode configurar [**automaticamente**](../../AspNetCore/Auto-API-Controllers.md) seus serviços de aplicativo como controladores de API MVC por convenção.
#### UI do Swagger
O modelo de inicialização está configurado para executar a [interface do usuário do swagger](https://swagger.io/tools/swagger-ui/) usando a biblioteca [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) . Execute o `Acme.BookStore.HttpApi.Host`aplicativo e insira `https://localhost:XXXX/swagger/`(substitua XXXX por sua própria porta) como URL no seu navegador.
Você verá alguns pontos de extremidade de serviço internos, bem como o `Book`serviço e seus pontos de extremidade no estilo REST:
`ApplicationLayoutComponent`configuração define o layout do aplicativo para a nova página. Se você deseja ver sua rota na barra de navegação (menu principal), também deve adicionar o `data`objeto com `name`propriedade à sua rota.
![página inicial dos livros](images/bookstore-initial-books-page-with-layout.png)
#### Componente da lista de livros
Primeiro, substitua pela `books.component.html`seguinte linha para colocar a saída do roteador:
```html
<router-outlet></router-outlet>
```
Em seguida, execute o comando abaixo no terminal na pasta raiz para gerar um novo componente, chamado book-list:
![página inicial da lista de livros](images/bookstore-initial-book-list-page.png)
#### Criar BooksState
Execute o seguinte comando no terminal para criar um novo estado, denominado `BooksState`:
```shell
yarn ng generate ngxs-schematic:state books
```
Este comando cria vários novos arquivos e edições `app.modules.ts`para importar o `NgxsModule`com o novo estado:
```js
// app.module.ts
import { BooksState } from './store/states/books.state';
@NgModule({
imports: [
//...
NgxsModule.forRoot([BooksState]),
],
//...
})
export class AppModule {}
```
#### Obter dados de livros do back-end
Primeiro, crie tipos de dados para mapear os dados que retornam do back-end (você pode verificar a interface do swagger ou a API do back-end para conhecer o formato dos dados).
Modifique o `books.ts`como mostrado abaixo:
```js
export namespace Books {
export interface State {
books: Response;
}
export interface Response {
items: Book[];
totalCount: number;
}
export interface Book {
name: string;
type: BookType;
publishDate: string;
price: number;
lastModificationTime: string;
lastModifierId: string;
creationTime: string;
creatorId: string;
id: string;
}
export enum BookType {
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry,
}
}
```
Adicionada `Book`interface que representa um objeto de livro e `BookType`enum representa uma categoria de livro.
#### BooksService
Agora, crie um novo serviço, nomeado `BooksService`para executar chamadas HTTP para o servidor:
> Consulte as [ações de despacho](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions) e [selecione](https://ngxs.gitbook.io/ngxs/concepts/select) na documentação do NGXS para obter mais informações sobre esses recursos do NGXS.
Substitua o `book-list.component.html`conteúdo conforme mostrado abaixo: