Dart: Otimização e Implantação de Projeto — DataPipeline
Última atualização: 2026-08-26
Código que roda não é suficiente; código que roda rápido, estável e com segurança é a entrega de um engenheiro. — Charlie
1. O que Você Aprenderá
- Análise e otimização de desempenho: Balanceamento de carga com Isolate / Backpressure em Stream / Otimização de memória
- Compilação AOT (
dart compile exe) e redução da saída - Configuração CI/CD: GitHub Actions +
dart test+dart analyze - Fluxo de publicação no pub.dev e documentação (README / CHANGELOG / API Docs)
- Aceitação final da Alice: relatório de teste de stress end-to-end com milhão de pedidos
2. A História Real de um Desenvolvedor
(1) A Dor: Roda mas Roda Lentamente
O DataPipeline processou 10K pedidos em apenas 0,3 segundos. Quando Alice o aplicou a um conjunto de dados de um milhão de pedidos para teste de stress, levou 45 segundos. Bob identificou os problemas: fragmentação desigual de Isolates (apenas 1 de 4 núcleos estava trabalhando), sem backpressure nas Streams causando pico de memória para 2GB, e um produto de 80MB compilado em modo debug.
(2) A Solução Otimizada
Fragmentação dinâmica de Isolates equilibrou a carga entre 4 núcleos, adicionar backpressure às Streams manteve a memória abaixo de 200MB, compilação AOT reduziu a saída para 15MB, e CI/CD automatizou testes e implantação.
flowchart LR A[Código Completo] --> B[Otimização de Desempenho] B --> C[Ajuste de Isolates] B --> D[Backpressure em Stream] B --> E[Análise de Memória] C --> F[Compilação AOT] F --> G[CI/CD] G --> H[GitHub Actions] H --> I[Publicação pub.dev] I --> J["v1.0.0"]
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
(3) Benefícios
- Processamento de um milhão de pedidos caiu de 45 segundos para 8 segundos
- Memória reduzida de 2GB para 180MB
- Saída de build encolheu de 80MB para 15MB
- Automação CI/CD garante qualidade a cada commit
3. Análise e Otimização de Desempenho
(1) Balanceamento de Carga com Isolate
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Fragmentação Dinâmica vs. Estática
import 'dart:isolate';
// Ruim: Fragmentação estática - trabalho desigual
List<List<T>> staticChunk<T>(List<T> data, int chunks) {
final size = (data.length / chunks).ceil();
return [
for (var i = 0; i < chunks; i++)
data.sublist(i * size, ((i + 1) * size).clamp(0, data.length))
];
}
// Bom: Work stealing dinâmico com SendPort
class DynamicIsolatePool {
final int _poolSize;
var _pendingChunks = <List<Map<String, dynamic>>>[];
final _results = <AnalysisResult>[];
var _activeWorkers = 0;
late Completer<List<AnalysisResult>> _completer;
DynamicIsolatePool({int poolSize = 4}) : _poolSize = poolSize;
Future<List<AnalysisResult>> process(
List<Map<String, dynamic>> allOrders, {
int chunkSize = 50000,
}) async {
_pendingChunks = [
for (var i = 0; i < allOrders.length; i += chunkSize)
allOrders.sublist(i, (i + chunkSize).clamp(0, allOrders.length))
];
_completer = Completer<List<AnalysisResult>>();
_activeWorkers = 0;
final workers = <_Worker>[];
for (var i = 0; i < _poolSize && _pendingChunks.isNotEmpty; i++) {
final chunk = _pendingChunks.removeAt(0);
workers.add(await _spawnWorker(chunk));
_activeWorkers++;
}
return _completer.future;
}
Future<_Worker> _spawnWorker(List<Map<String, dynamic>> initialChunk) async {
final receivePort = ReceivePort();
final isolate = await Isolate.spawn(_workerEntry, receivePort.sendPort);
final sendPort = await receivePort.first as SendPort;
final responsePort = ReceivePort();
sendPort.send(_WorkMessage(data: initialChunk, responsePort: responsePort.sendPort));
responsePort.listen((result) {
_results.add(result as AnalysisResult);
_activeWorkers--;
if (_pendingChunks.isNotEmpty) {
final nextChunk = _pendingChunks.removeAt(0);
sendPort.send(_WorkMessage(data: nextChunk, responsePort: responsePort.sendPort));
_activeWorkers++;
} else if (_activeWorkers == 0) {
isolate.kill(priority: Isolate.immediate);
receivePort.close();
responsePort.close();
_completer.complete(_results);
}
});
return _Worker(isolate: isolate, sendPort: sendPort);
}
static void _workerEntry(SendPort mainSendPort) {
final receivePort = ReceivePort();
mainSendPort.send(receivePort.sendPort);
receivePort.listen((message) {
if (message is _WorkMessage) {
final orders = message.data.map((m) => Order(
id: m['id'] as String,
amount: (m['amount'] as num).toDouble(),
status: m['status'] as String,
category: m['category'] as String,
)).toList();
final result = const OrderAnalyzer().analyze(orders);
message.responsePort.send(result);
}
});
}
}
class _Worker {
final Isolate isolate;
final SendPort sendPort;
const _Worker({required this.isolate, required this.sendPort});
}
class _WorkMessage {
final List<Map<String, dynamic>> data;
final SendPort responsePort;
const _WorkMessage({required this.data, required this.responsePort});
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
| Área de Otimização | Antes | Depois | Melhoria |
|---|---|---|---|
| Estratégia de Fragmentação | Divisão igual estática | Work stealing dinâmico | 30% mais rápido |
| Tamanho do Chunk | 1 chunk/worker | 50K itens/chunk | Memória pela metade |
| Contagem de Workers | Fixo 4 | Adaptativo aos núcleos da CPU | Mais rápido em multi-core |
(2) Backpressure em Stream
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Controle de Backpressure
import 'dart:async';
class BackpressurePipeline {
final DataSource _source;
final OrderParser _parser;
final int _maxConcurrent;
BackpressurePipeline({
required DataSource source,
required OrderParser parser,
int maxConcurrent = 1000,
}) : _source = source,
_parser = parser,
_maxConcurrent = maxConcurrent;
Future<AnalysisResult> execute() async {
final orders = <Order>[];
var pending = 0;
final completer = Completer<void>();
final subscription = _source.readLines().listen(
(line) {
try {
orders.add(_parser.parse(line));
} on FormatException {
return;
}
pending++;
if (pending >= _maxConcurrent) {
subscription.pause();
}
},
onDone: () {
completer.complete();
},
onError: (e) {
completer.completeError(e);
},
);
await completer.future;
return const OrderAnalyzer().analyze(orders);
}
// Stream pausável com controle de buffer
Stream<Order> streamWithBackpressure() async* {
var buffer = <Order>[];
await for (final line in _source.readLines()) {
try {
buffer.add(_parser.parse(line));
} on FormatException {
continue;
}
if (buffer.length >= _maxConcurrent) {
for (final order in buffer) {
yield order;
}
buffer = [];
}
}
for (final order in buffer) {
yield order;
}
}
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
| Estratégia de Backpressure | Descrição | Caso de Uso |
|---|---|---|
| pause/resume | Pausa a stream fonte | Consumidor processa lentamente |
| Limite de buffer | Buffer de tamanho fixo | Uso de memória controlável |
| drop latest | Descarta dados que excedem o limite | Perda tolerável em monitoramento em tempo real |
(3) Otimização de Memória
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Técnicas de Análise de Memória
import 'dart:math';
class MemoryOptimizer {
// Usar iterables em vez de lists quando possível
static Iterable<Order> lazyParse(Iterable<String> lines, OrderParser parser) sync* {
for (final line in lines) {
try {
yield parser.parse(line);
} on FormatException {
continue;
}
}
}
// Processamento em chunks para limitar memória
static Future<AnalysisResult> processInChunks(
Stream<String> lines,
OrderParser parser, {
int chunkSize = 100000,
}) async {
var chunk = <Order>[];
var totalResult = _EmptyAnalysisResult();
await for (final line in lines) {
try {
chunk.add(parser.parse(line));
} on FormatException {
continue;
}
if (chunk.length >= chunkSize) {
final partial = const OrderAnalyzer().analyze(chunk);
totalResult = totalResult.merge(partial);
chunk = [];
}
}
if (chunk.isNotEmpty) {
final partial = const OrderAnalyzer().analyze(chunk);
totalResult = totalResult.merge(partial);
}
return totalResult.toAnalysisResult();
}
}
class _EmptyAnalysisResult {
int totalOrders = 0;
int completedOrders = 0;
double revenue = 0;
double tax = 0;
final Map<String, double> revenueByCategory = {};
_EmptyAnalysisResult merge(AnalysisResult other) {
totalOrders += other.totalOrders;
completedOrders += other.completedOrders;
revenue += other.revenue;
tax += other.tax;
for (final entry in other.revenueByCategory.entries) {
revenueByCategory.update(entry.key, (v) => v + entry.value, ifAbsent: () => entry.value);
}
return this;
}
AnalysisResult toAnalysisResult() => AnalysisResult(
totalOrders: totalOrders,
completedOrders: completedOrders,
revenue: revenue,
tax: tax,
total: revenue + tax,
revenueByCategory: Map.unmodifiable(revenueByCategory),
);
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
| Técnica de Otimização | Efeito | Caso de Uso |
|---|---|---|
| Iterable Lazy | Sem pré-carregamento de todos os dados | Passagem de dados item a item da fonte ao consumidor |
| Processamento em Chunks | Uso de memória constante | Processamento de milhões de registros |
| Mesclagem de Resultados | Resultados intermediários pequenos | Agregação de estatísticas após fragmentação |
4. Compilação AOT e Redução da Saída
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Comandos de Compilação e Otimização
# Build debug (grande, inclui informações de debug)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline_debug
# Build release (otimizado, menor)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline \
--define=MODE=release
# Verificar tamanho do binário
ls -lh build/
# Remover símbolos de debug (redução adicional de tamanho)
# No Linux/macOS:
strip build/data_pipeline
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
| Modo de Compilação | Comando | Tamanho da Saída | Velocidade de Inicialização |
|---|---|---|---|
JIT (dart run) |
dart run bin/main.dart |
0 (roda fonte) | Lenta |
| AOT exe | dart compile exe |
~15MB | Rápida |
| AOT aot-snapshot | dart compile aot-snapshot |
~8MB | Mais rápida (requer dartaotruntime) |
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Configuração em Tempo de Compilação
// Imports condicionais para tree shaking
const _isRelease = bool.fromEnvironment('MODE.release');
void log(String message) {
if (!_isRelease) {
print('[DEBUG] $message');
}
}
// Esta classe inteira será removida por tree shaking no modo release
class DebugLogger {
void log(String message) {
if (!_isRelease) {
print('[DEBUG] ${DateTime.now()}: $message');
}
}
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
5. CI/CD — GitHub Actions
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Configuração CI Completa
# .github/workflows/ci.yml
name: DataPipeline CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
analyze-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
sdk: [stable, beta]
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
with:
sdk: ${{ matrix.sdk }}
- name: Instalar dependências
run: dart pub get
- name: Verificar formatação
run: dart format --output=none --set-exit-if-changed .
- name: Analisar código
run: dart analyze --fatal-infos
- name: Executar testes
run: dart test --coverage=coverage
- name: Enviar cobertura
uses: codecov/codecov-action@v3
with:
files: coverage/lcov.info
build-and-release:
needs: analyze-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- name: Instalar dependências
run: dart pub get
- name: Compilar binário AOT
run: |
dart compile exe bin/data_pipeline.dart -o build/data_pipeline
- name: Executar teste de integração
run: |
./build/data_pipeline analyze --source csv --input test/fixtures/sample.csv
- name: Enviar artefato binário
uses: actions/upload-artifact@v4
with:
name: data_pipeline-linux
path: build/data_pipeline
publish:
needs: build-and-release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- name: Instalar dependências
run: dart pub get
- name: Publicar no pub.dev
run: dart pub publish --force
env:
PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }}
| Estágio CI | Verificações Realizadas | Ação em Caso de Falha |
|---|---|---|
| Formatação | dart format |
Auto-formatar PR |
| Análise Estática | dart analyze |
Bloquear merge |
| Testes Unitários | dart test |
Bloquear merge |
| Teste de Integração | Binário AOT end-to-end | Bloquear publicação |
| Publicação | dart pub publish |
Acionado apenas por tag |
6. Publicação no pub.dev
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Configuração de Publicação pubspec.yaml
name: data_pipeline
description: Ferramenta CLI de análise de dados de e-commerce - processa pedidos em nível de milhão com Stream + Isolate
version: 1.0.0
homepage: https://github.com/bob/datapipeline
repository: https://github.com/bob/datapipeline
issue_tracker: https://github.com/bob/datapipeline/issues
environment:
sdk: ^3.0.0
dependencies:
args: ^2.4.2
http: ^1.1.0
csv: ^6.0.0
dev_dependencies:
test: ^1.24.0
build_runner: ^2.4.0
json_serializable: ^6.7.0
executables:
data_pipeline: data_pipeline
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: CHANGELOG.md
# CHANGELOG
---
## 7. Notas de Release
### (1) 1.0.0 - 2025-01-15
#### Funcionalidades
- Suporte a fonte de dados CSV e JSON
- Pipeline baseado em Stream com backpressure
- Pool de Isolates para processamento paralelo
- CLI com subcomandos analyze/export/validate
- Arquitetura Pattern Matching + classe Sealed
### (2) Desempenho
- 1M pedidos processados em ~8 segundos (4 núcleos)
- Uso de memória abaixo de 200MB para 1M registros
### (3) Testes
- 95% de cobertura de código
- Testes unitários + integração + E2E
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Checklist Pré-Publicação
# Checklist de pré-publicação
# 1. Publicação dry run
dart pub publish --dry-run
# 2. Verificar pontuação no pub.dev
dart pub publish --dry-run 2>&1 | grep -E "score|warning|error"
# 3. Gerar docs da API
dart doc .
# 4. Verificar conteúdo do pacote
dart pub pack # cria .tar.gz para inspeção
# 5. Publicação final
dart pub publish
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
| Etapa de Publicação | Comando | Finalidade |
|---|---|---|
| Dry Run | dart pub publish --dry-run |
Verificar conteúdo do pacote |
| Gerar Docs | dart doc . |
Documentação da API |
| Verificação de Versão | pubspec.yaml version |
Versionamento semântico |
| Publicar | dart pub publish |
Enviar para pub.dev |
7. Geração de Documentação da API
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Padrões de Comentários de Documentação
/// Analisa dados de pedidos de e-commerce e produz resultados estatísticos.
///
/// Use [OrderAnalyzer] como ponto de entrada principal para análise de dados.
/// Configure a [taxRate] com base na região de destino.
///
/// Exemplo:
/// ```dart
/// final analyzer = OrderAnalyzer(taxRate: 0.08);
/// final result = analyzer.analyze(orders);
/// print('Receita: \$${result.revenue} USD');
/// ```
///
/// Veja também:
/// - [AnalysisResult] para a estrutura de saída
/// - [Order] para o modelo de entrada
class OrderAnalyzer {
/// Cria um analisador com a [taxRate] fornecida.
///
/// Padrão é 0.08 (taxa de imposto dos EUA de 8%).
/// Use taxas específicas por região: US=0.08, EU=0.20, UK=0.15.
const OrderAnalyzer({this.taxRate = 0.08});
/// A taxa de imposto aplicada a pedidos completados.
final double taxRate;
/// Analisa uma lista de [orders] e retorna estatísticas agregadas.
///
/// Apenas pedidos com status 'completed' são incluídos nos cálculos
/// de receita. Pedidos com outros status são contados em
/// [AnalysisResult.totalOrders] mas excluídos da receita.
AnalysisResult analyze(List<Order> orders) {
// ... implementação
}
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
8. Exemplo Completo: Benchmark de Desempenho
// ============================================
// Benchmark de Desempenho do DataPipeline
// Teste de Stress End-to-End da Alice
// ============================================
import 'dart:async';
import 'dart:io';
class Benchmark {
final String name;
final Stopwatch _stopwatch;
Benchmark(this.name) : _stopwatch = Stopwatch();
Future<T> run<T>(Future<T> Function() action) async {
_stopwatch.reset();
_stopwatch.start();
final result = await action();
_stopwatch.stop();
print('$name: ${_stopwatch.elapsedMilliseconds}ms');
return result;
}
}
// Gerar dados de teste
List<String> generateOrders(int count) {
final categories = ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'];
final statuses = ['completed', 'completed', 'completed', 'pending', 'cancelled'];
final regions = ['US', 'EU', 'UK', 'JP'];
return List.generate(count, (i) {
final category = categories[i % categories.length];
final status = statuses[i % statuses.length];
final region = regions[i % regions.length];
final amount = (100 + (i * 37) % 5000).toDouble();
return 'ORD-${(i + 1).toString().padLeft(7, '0')},$amount,$status,$category,$region';
});
}
void main() async {
print('=== Benchmark de Desempenho do DataPipeline ===\n');
final sizes = [10000, 100000, 1000000];
for (final size in sizes) {
final label = size >= 1000000 ? '${size ~/ 1000000}M' : '${size ~/ 1000}K';
print('--- $label pedidos ---');
// Gerar e salvar em arquivo temporário
final data = generateOrders(size);
final tempFile = File('${Directory.systemTemp.path}/bench_${label}.csv');
await tempFile.writeAsString(data.join('\n'));
// Benchmark 1: Processamento Stream
final streamResult = await Benchmark('Stream ($label)').run(() async {
final source = CsvSource(path: tempFile.path);
final parser = const CsvOrderParser();
final pipeline = Pipeline(source: source, parser: parser);
return pipeline.execute();
});
// Benchmark 2: Processamento em chunks com eficiência de memória
final chunkedResult = await Benchmark('Chunked ($label)').run(() async {
final source = CsvSource(path: tempFile.path);
return MemoryOptimizer.processInChunks(
source.readLines(),
const CsvOrderParser(),
chunkSize: 50000,
);
});
// Verificar se os resultados coincidem
final streamRev = streamResult.revenue.toStringAsFixed(2);
final chunkedRev = chunkedResult.revenue.toStringAsFixed(2);
print('Receita coincide: ${streamRev == chunkedRev ? "PASS" : "FAIL"}');
print('Receita: \$${streamRev} USD\n');
await tempFile.delete();
}
// Benchmark 3: Verificação de tamanho do binário AOT
print('--- Artefatos de Build ---');
final exe = File('build/data_pipeline');
if (await exe.exists()) {
final sizeKB = await exe.length() ~/ 1024;
print('Binário AOT: ${sizeKB}KB');
} else {
print('Binário AOT: não compilado (execute: dart compile exe bin/data_pipeline.dart)');
}
print('\n=== Benchmark Completo ===');
print('Critérios de aceitação da Alice: 1M pedidos < 10s, memória < 300MB, binário < 20MB');
}
// ---- Stubs necessários (importados de lições anteriores) ----
sealed class DataSource {
const DataSource();
String get displayName;
Stream<String> readLines();
}
class CsvSource extends DataSource {
final String path;
const CsvSource({required this.path});
@override
String get displayName => 'CSV: $path';
@override
Stream<String> readLines() =>
File(path).openRead().transform(utf8.decoder).transform(const LineSplitter());
}
abstract class OrderParser {
const OrderParser();
Order parse(String raw);
}
class CsvOrderParser extends OrderParser {
const CsvOrderParser();
@override
Order parse(String raw) {
final parts = raw.split(',');
if (parts.length < 4) throw FormatException('Linha inválida');
return Order(
id: parts[0].trim(),
amount: double.parse(parts[1].trim()),
status: parts[2].trim(),
category: parts[3].trim(),
region: parts.length > 4 ? parts[4].trim() : 'US',
);
}
}
class Order {
final String id;
final double amount;
final String status;
final String category;
final String region;
const Order({required this.id, required this.amount, required this.status, required this.category, this.region = 'US'});
double get tax => amount * 0.08;
}
class AnalysisResult {
final int totalOrders;
final int completedOrders;
final double revenue;
final double tax;
final double total;
final Map<String, double> revenueByCategory;
const AnalysisResult({required this.totalOrders, required this.completedOrders, required this.revenue, required this.tax, required this.total, required this.revenueByCategory});
}
class OrderAnalyzer {
const OrderAnalyzer();
AnalysisResult analyze(List<Order> orders) {
final completed = orders.where((o) => o.status == 'completed').toList();
final revenue = completed.fold<double>(0, (s, o) => s + o.amount);
final tax = completed.fold<double>(0, (s, o) => s + o.tax);
final byCategory = <String, double>{};
for (final o in completed) {
byCategory.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
}
return AnalysisResult(totalOrders: orders.length, completedOrders: completed.length, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
}
}
class Pipeline {
final DataSource source;
final OrderParser parser;
const Pipeline({required this.source, required this.parser});
Future<AnalysisResult> execute() async {
final orders = <Order>[];
await for (final line in source.readLines()) {
try { orders.add(parser.parse(line)); } on FormatException { continue; }
}
return const OrderAnalyzer().analyze(orders);
}
}
class MemoryOptimizer {
static Future<AnalysisResult> processInChunks(Stream<String> lines, OrderParser parser, {int chunkSize = 50000}) async {
var chunk = <Order>[];
int totalOrders = 0, completedOrders = 0;
double revenue = 0, tax = 0;
final byCategory = <String, double>{};
await for (final line in lines) {
try { chunk.add(parser.parse(line)); } on FormatException { continue; }
if (chunk.length >= chunkSize) {
final r = const OrderAnalyzer().analyze(chunk);
totalOrders += r.totalOrders;
completedOrders += r.completedOrders;
revenue += r.revenue;
tax += r.tax;
for (final e in r.revenueByCategory.entries) {
byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
}
chunk = [];
}
}
if (chunk.isNotEmpty) {
final r = const OrderAnalyzer().analyze(chunk);
totalOrders += r.totalOrders;
completedOrders += r.completedOrders;
revenue += r.revenue;
tax += r.tax;
for (final e in r.revenueByCategory.entries) {
byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
}
}
return AnalysisResult(totalOrders: totalOrders, completedOrders: completedOrders, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
}
}
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
9. Relatório de Aceitação da Alice
| Critério de Aceitação | Alvo | Real | Status |
|---|---|---|---|
| Tempo de Processamento 1M Pedidos | < 10s | ~8s | PASS |
| Pico de Uso de Memória | < 300MB | ~180MB | PASS |
| Tamanho da Saída AOT | < 20MB | ~15MB | PASS |
| Cobertura de Testes | > 90% | 95% | PASS |
dart analyze |
0 warnings | 0 | PASS |
dart format |
100% | 100% | PASS |
| Cobertura de Documentação | 100% da API pública | 100% | PASS |
| Pontuação pub.dev | > 120 | 140 | PASS |
Alice: "DataPipeline v1.0 aceito! Um milhão de pedidos processados em 8 segundos, memória mantida abaixo de 200MB, saída de build AOT compacta, e automação CI/CD completa. Pronto para release!"
❓ Perguntas Frequentes
P: Por que a saída do build AOT é tão grande? R: A saída AOT inclui uma VM Dart simplificada + código nativo compilado. Use
strippara remover símbolos de debug etree shakingpara remover código não utilizado. Um build AOT típico de CLI Dart tem 10-20MB.
P: O custo de criação de Isolate é significativo? R:
Isolate.spawnleva ~50-100ms,Isolate.runleva ~30ms. Para tarefas de longa duração, use um Isolate Pool para reutilizar workers e evitar criação/destruição frequente.
P: Como o backpressure de Stream e os Isolates trabalham juntos? R: O Isolate principal lê a Stream e a armazena em buffer até um tamanho fixo, pausando quando cheio. Os Worker Isolates consomem os dados armazenados em buffer e notificam o Isolate principal para retomar quando terminam. É similar ao padrão produtor-consumidor.
P: Como posso melhorar minha pontuação no pub.dev? R: Garanta: 1) pubspec.yaml completo (homepage/repository/description); 2) README.md detalhado; 3)
dart analyzecom zero warnings; 4) Cobertura de testes > 80%; 5) Comentários de documentação cobrindo todas as APIs públicas.
P: Como armazenar em cache dependências no GitHub Actions? R: Use
actions/cachepara armazenar em cache~/.pub-cache, com a chave baseada no hash dopubspec.lock. Primeira execução leva ~30s, cache hit leva ~3s.
P: Como testar o binário compilado em AOT no CI? R: Após
dart compile exe, execute./build/data_pipeline analyze --input test.csvdiretamente, verificando o código de saída e a saída. Isso é mais próximo do ambiente de produção do quedart run.
📖 Resumo
- Trio de otimização de desempenho: Fragmentação dinâmica de Isolate, controle de backpressure em Stream, processamento em chunks para limites de memória
- Compilação AOT dá ao CLI Dart velocidade de inicialização nativa;
strip+tree shakingreduzem a saída - Automação CI/CD: Formatar → Analisar → Testar → Compilar → Teste de Integração → Publicar
- Publicar no pub.dev requer pubspec.yaml completo, README, CHANGELOG e documentação da API
- Alice aceitou: 8 segundos para um milhão de pedidos, 180MB de memória, 15MB de saída de build
📝 Exercícios
- Básico (Dificuldade ⭐): Adicione um Makefile/script para
dart compile exeao DataPipeline, permitindo compilação do build release com um clique e saída do tamanho do arquivo. - Intermediário (Dificuldade ⭐⭐): Implemente o mecanismo pause/resume para backpressure em Stream. Verifique com processamento de dados de 10K pedidos: a memória não aumenta durante a pausa e o processamento retoma corretamente.
- Desafio (Dificuldade ⭐⭐⭐): Configure um pipeline CI completo do GitHub Actions: verificação de formatação + análise estática + teste unitário + compilação AOT + teste de integração. Envie para o GitHub para verificar se o pipeline passa.
← Lição Anterior | Curso Completo — Parabéns por concluir todas as 24 lições!