APIs e Microsserviços

4 abr, 2014

Construa APIs que você não odeia: população útil da base de dados – Parte 01

Publicidade

Pouco tempo atrás, escrevi um artigo chamado Criando uma API respeitável, que foi principalmente uma lista de brincadeira de coisas que me irritaram que eu encontrei em outras APIs, ou que eu mesmo tinha feito e usei meu superpoder de retrospectiva combinado com sarcasmo para criar um conjunto de regras para você seguir ao construir APIs.

A combinação de palavras e feias e engraçadas utilizadas nesse artigo fez com que ele “descesse” muito mais suave, mas, com certeza, faltou um pouco de substância. Eu sempre quis transformar aquilo em algo com muito mais profundidade.

Vamos começar pelo começo. Bem, um diagrama de entidade-relacionamento é, provavelmente, o primeiro passo, a menos que você seja um cowboy lunático, mas eles são chato pra caramba. Seja como for, vamos começar com o povoamento do seu banco de dados.

O que é povoamento do banco de dados?

A ideia é bastante simples. Você tem o seu esquema de banco de dados construído de alguma forma, através de um arquivo .sql que você importou, ou uma série de migrações, ou usa o mongo e sua aplicação apenas o construirá magicamente para você. Seja o que for, você tem um banco de dados vazio e precisa preenchê-lo com dados. Isso é muitas vezes chamado de “dados fictícios”.

Esses dados poderiam ser as categorias para o seu aplicativo, os usuários de teste, as entradas de conteúdo com um monte de comentários, locais falsos disponíveis para check-in, notificações falsas para exibir no aplicativo para iPhone (um de cada tipo), ou pagamentos de cartão de crédito em vários estágios de processamento – alguns completos, alguns pela metade e outros parecendo super-fraudulentos.

Isso tudo é muito útil para que você não precise perder tempo criando tudo manualmente todas as vezes, porque os dados que você insere são quase sempre meio idiotas e muitas vezes se esquece das coisas que realmente deveriam ter sido consideradas.

Um uso para dados fictícios é testar (esse é o próximo artigo), fazendo com que freelancers/terceiros aumentem a velocidade do conteúdo útil e evitem a tentação de copiar dados de tempo real para seu ambiente de desenvolvimento.

Por que é ruim utilizar dados de produção no desenvolvimento?

Você já escreveu um script que envia e-mails e utilizava alguma cópia fictícia, enquanto você estava criando? Já usou algumas palavras malcriadas nesse conteúdo? Já enviou acidentalmente esse e-mail para 10 mil endereços de e-mail de clientes reais? Já foi demitido por fazer a empresa perder £200 mil?

Eu não nunca fui, mas eu conheço um cara que foi. Não seja aquele cara.

Dane-se isso. Que dados devemos usar?

Lixo! Use algo absolutamente sem sentido para sua base de dados de desenvolvimento, mas algo sem sentido de tipo, tamanho e formatos corretos. Isso pode ser feito com uma pequena e divertida biblioteca chamada Faker de François Zaninotto, que é uma biblioteca maravilhosa que pode essencialmente gerar qualquer tipo de besteira.

A Kapture usa o Laravel, que tem a alegria de possuir população de banco de dados já incluída. Isso é essencialmente uma tarefa bem feita em linha de comando que quase todo o framework PHP moderno terá (ou deve ter); portanto, os princípios são aplicáveis para todos.

Quebre seus populadores de banco de dados em pedaços. Isso não precisa ser “um povoador-por-tabela”, mas também pode ser. A razão pela qual eu não tento manter essa regra é que, por vezes, seus dados precisam ser construídos ao mesmo tempo em que outros tipos de dados, por isso, para nós, usuários são criados no mesmo “povoador” que suas configurações, tokens OAuth e em que os dados de amizade são feitos. Colocando essas coisas em múltiplos povoadores apenas para manter as coisas arrumadas seria um exercício de futilidade e retardaria tudo sem nenhum motivo.

Então, essa é uma versão drasticamente simplificada de nosso semeador de usuário todo de uma vez, ignorando a estrutura específica do Laravel. Se estiver usando o L4, apenas jogue isso no seu método run().

$faker = Faker\Factory::create();

for ($i = 0; $i < Config::get('seeding.users'); $i++) {

    $user = User::create([
        'name'               => $faker->name,
        'email'              => $faker->email,
        'active'             => $i === 0 ? true : rand(0, 1),
        'gender'             => rand(0, 1) ? 'male' : 'female',
        'timezone'           => mt_rand(-10, 10),
        'birthday'           => rand(0, 1) ? $faker->dateTimeBetween('-40 years', '-18 years') : null,
        'location'           => rand(0, 1) ? "{$faker->city}, {$faker->state}" : null,
        'had_feedback_email' => (bool) rand(0, 1),
        'sync_name_bio'      => (bool) rand(0, 1),
        'bio'                => $faker->sentence(100),
        'picture_url'        => $this->picture_url[rand(0, 19)],
    ]);
}

Então, o que temos aqui? Vejamos esta seção:

$faker = Faker\Factory::create();

Uma instância do Faker, ou de um artista de mentira.

for ($i = 0; $i < Config::get('seeding.users'); $i++) {

Vamos querer um certo número de usuários, mas eu recomendo que você tenha um pouco menos do que no desenvolvimento do que tem nos testes ou no staging, por conta do tempo.

$user = User::create([
       'name'               => $faker->name,
       'email'              => $faker->email,

Crie nomes e e-mails aleatórios. Não temos de definir o conjunto de dados aleatórios para ele usar, porque É MÁGICA!

'active'             => $i === 0 ? true : rand(0, 1),

Ok, eu menti, nosso lixo não é 100% aleatório. Queremos que o usuário de número 1 seja ativo para testes mais tarde.

'gender'             => rand(0, 1) ? 'male' : 'female',

A igualdade de gênero é importante.

'timezone'           => mt_rand(-10, 10),

Nosso primeiro desenvolvedor decidiu que salvar fusos horários como um inteiro foi uma coisa inteligente a se fazer. Como você lida com países com diferença de +4,45 de fuso horário, amigo? Vou precisar refatorar isso, mas está bom por enquanto.

'birthday'           => rand(0, 1) ? $faker->dateTimeBetween('-40 years', '-18 years') : null,

Usuários de todas as idades do nosso público-alvo.

'location'           => rand(0, 1) ? "{$faker->city}, {$faker->state}" : null,

Nos dê um nome de cidade e do estado. Isso funciona bem com os países estrangeiros também, o que é bem legal.

'hadfeedbackemail' => (bool) rand(0, 1),
'syncnamebio'      => (bool) rand(0, 1),

Algumas flags de usuários que não importam muito. True ou false, seja o que for.

'bio'                => $faker->sentence(100),

Faça uma frase com 100 caracteres.

Isso é tudo

Você vai acabar fazendo um monte desses arquivos e vai querer preencher com dados praticamente todas as tabelas que tem. Você também vai querer dizer ao seu populador de banco de dados para limpar todas as tabelas que você vai preencher. Faça isso globalmente logo no início do processo, não limpe cada tabela, na parte superior de cada populador ou você estará acabando com o conteúdo na tabela de outros populadores no mesmo processo.

Exemplo de um sistema global no Laravel 4:

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        if (App::environment() === 'production') {
            exit('I just stopped you getting fired. Love Phil');
        }

    Eloquent::unguard();

    $tables = [
        'locations',
        'merchants',
        'opps',
        'opps_locations',
        'moments',
        'rewards',
        'users',
        'oauth_sessions',
        'notifications',
        'favorites',
        'settings',
        'friendships',
        'impressions',
    ];

    foreach ($tables as $table) {
        DB::table($table)->truncate();
    }

    $this->call('MerchantTableSeeder');
    $this->call('PlaceTableSeeder');
    $this->call('UserTableSeeder');
    $this->call('OppTableSeeder');
    $this->call('MomentTableSeeder');
}

}

Isso apaga tudo, em seguida, executa outras classes populadoras.

Então apenas execute $ php artisan db:seed, e ele sabe tudo o que é preciso.

Quando executar isso

Isso é executado sempre que um desenvolvedor na equipe quer novos dados em seu sistema, em intervalos aleatórios no servidor de preparação e automaticamente no servidor de testes jenkins quando fizer o deploy de novas correções da API.

Mais informações sobre automação de testes com Jenkins e Behat chegarão em breve, e muito mais para virá depois disso, com a inclusão de como representar sua saída de dados, como lidar com dados de hierárquicos (respeitando contexto e evitando hierarquização ilimitado e vazamentos de memória) e autenticação com servidor OAuth 2 da PHP Leage of Extraordinary Packages.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em  http://philsturgeon.co.uk/blog/2013/11/build-apis-part-1-useful-database-seeding