Skip to main content

Overview

The Beehive repository layer follows Clean Architecture principles by defining an abstract interface in the domain layer and providing a concrete implementation in the data layer. This abstraction enables testability, flexibility, and separation of concerns.

BeehiveRepository Interface

The abstract repository interface defines the contract for beehive data operations.

Interface Definition

abstract class BeehiveRepository {
  Future<Either<Failure, List<Beehive>>> getBeehivesByApiary(String apiaryId);
  
  Future<Either<Failure, Beehive>> createBeehive(
    String apiaryId,
    int beehiveNumber,
    String? activityLevel,
    String? beePopulation,
    int? foodFrames,
    int? broodFrames,
    String? hiveStatus,
    String? healthStatus,
    String? hasProductionChamber,
    String? observations,
  );
  
  Future<Either<Failure, Beehive>> updateBeehive(
    String beehiveId,
    String apiaryId,
    int? beehiveNumber,
    String? activityLevel,
    String? beePopulation,
    int? foodFrames,
    int? broodFrames,
    String? hiveStatus,
    String? healthStatus,
    String? hasProductionChamber,
    String? observations,
  );
  
  Future<Either<Failure, void>> deleteBeehive(
    String beehiveId,
    String apiaryId,
  );
}

Methods

getBeehivesByApiary

Retrieves all beehives for a specific apiary.
apiaryId
String
required
The ID of the apiary to fetch beehives for
return
Future<Either<Failure, List<Beehive>>>
Returns a list of beehives on success, or a failure on error
Example:
final result = await repository.getBeehivesByApiary('apiary_123');

result.fold(
  (failure) => print('Failed to fetch beehives: $failure'),
  (beehives) => print('Retrieved ${beehives.length} beehives'),
);

createBeehive

Creates a new beehive in the specified apiary.
apiaryId
String
required
ID of the parent apiary
beehiveNumber
int
required
Sequential number for the beehive
activityLevel
String?
Activity level classification
beePopulation
String?
Population size classification
foodFrames
int?
Number of food frames
broodFrames
int?
Number of brood frames
hiveStatus
String?
Operational status of the hive
healthStatus
String?
Health condition of the beehive
hasProductionChamber
String?
Production chamber presence indicator
observations
String?
Additional notes
return
Future<Either<Failure, Beehive>>
Returns the created beehive on success, or a failure on error
Example:
final result = await repository.createBeehive(
  'apiary_123',
  15,
  'Alta',
  'Media',
  3,
  7,
  'Cámara de cría y producción',
  'Ninguno',
  'Si',
  'New colony from recent split',
);

result.fold(
  (failure) => print('Creation failed: $failure'),
  (beehive) => print('Created beehive with ID: ${beehive.id}'),
);

updateBeehive

Updates an existing beehive’s information.
beehiveId
String
required
ID of the beehive to update
apiaryId
String
required
ID of the parent apiary (for authorization)
beehiveNumber
int?
Updated beehive number
activityLevel
String?
Updated activity level
beePopulation
String?
Updated population classification
foodFrames
int?
Updated food frames count
broodFrames
int?
Updated brood frames count
hiveStatus
String?
Updated hive status
healthStatus
String?
Updated health status
hasProductionChamber
String?
Updated production chamber status
observations
String?
Updated observations
return
Future<Either<Failure, Beehive>>
Returns the updated beehive on success, or a failure on error
Example:
final result = await repository.updateBeehive(
  'beehive_789',
  'apiary_123',
  null,  // Keep current beehive number
  'Media',  // Changed activity level
  null,  // Keep current population
  5,  // Updated food frames
  null,  // Keep current brood frames
  null,  // Keep current hive status
  'Presencia barroa',  // Updated health status
  null,  // Keep production chamber status
  'Treatment started for varroa',  // Updated observations
);

result.fold(
  (failure) => print('Update failed: $failure'),
  (beehive) => print('Successfully updated beehive'),
);

deleteBeehive

Deletes a beehive from an apiary.
beehiveId
String
required
ID of the beehive to delete
apiaryId
String
required
ID of the parent apiary (required for backend authorization)
return
Future<Either<Failure, void>>
Returns void on success, or a failure on error
Example:
final result = await repository.deleteBeehive(
  'beehive_789',
  'apiary_123',
);

result.fold(
  (failure) => print('Deletion failed: $failure'),
  (_) => print('Beehive successfully deleted'),
);

BeehiveRepositoryImpl

The concrete implementation of the BeehiveRepository interface.

Implementation Details

class BeehiveRepositoryImpl implements BeehiveRepository {
  final BeehiveRemoteDataSource remoteDataSource;
  final AuthLocalDataSource localDataSource;

  BeehiveRepositoryImpl({
    required this.remoteDataSource,
    required this.localDataSource,
  });
  
  // Implementation methods...
}

Dependencies

remoteDataSource
BeehiveRemoteDataSource
required
Handles HTTP communication with the backend API
localDataSource
AuthLocalDataSource
required
Provides authentication tokens from local storage

Authentication Flow

All repository methods follow this authentication pattern:
// 1. Retrieve authentication token
final token = await localDataSource.getToken();
if (token == null) {
  return const Left(
    AuthFailure('No se encontró el token de autenticación.'),
  );
}

// 2. Call remote data source with token
final result = await remoteDataSource.someMethod(params, token);

// 3. Return result wrapped in Either
return Right(result);

Error Handling

The implementation catches exceptions and converts them to appropriate Failure types:

Complete Implementation Example

Dependency Injection Setup

Example setup using Riverpod:
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Data source providers
final beehiveRemoteDataSourceProvider = Provider<BeehiveRemoteDataSource>(
  (ref) => BeehiveRemoteDataSource(ref.watch(httpClientProvider)),
);

final authLocalDataSourceProvider = Provider<AuthLocalDataSource>(
  (ref) => AuthLocalDataSource(ref.watch(secureStorageProvider)),
);

// Repository provider
final beehiveRepositoryProvider = Provider<BeehiveRepository>(
  (ref) => BeehiveRepositoryImpl(
    remoteDataSource: ref.watch(beehiveRemoteDataSourceProvider),
    localDataSource: ref.watch(authLocalDataSourceProvider),
  ),
);

Testing

The repository abstraction enables easy unit testing:
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

class MockBeehiveRepository extends Mock implements BeehiveRepository {}

void main() {
  late MockBeehiveRepository mockRepository;
  
  setUp(() {
    mockRepository = MockBeehiveRepository();
  });
  
  test('should return list of beehives when call succeeds', () async {
    // Arrange
    final beehives = [Beehive(id: '1', apiaryId: 'apiary_123')];
    when(mockRepository.getBeehivesByApiary('apiary_123'))
        .thenAnswer((_) async => Right(beehives));
    
    // Act
    final result = await mockRepository.getBeehivesByApiary('apiary_123');
    
    // Assert
    expect(result.isRight, true);
    expect(result.right, beehives);
  });
  
  test('should return failure when authentication fails', () async {
    // Arrange
    when(mockRepository.getBeehivesByApiary('apiary_123'))
        .thenAnswer((_) async => Left(AuthFailure('No token')));
    
    // Act
    final result = await mockRepository.getBeehivesByApiary('apiary_123');
    
    // Assert
    expect(result.isLeft, true);
    expect(result.left, isA<AuthFailure>());
  });
}

Architecture Benefits

Location

Source files:
  • Interface: /lib/feature/beehive/domain/repositories/beehive_repository.dart
  • Implementation: /lib/feature/beehive/data/repositories/beehive_repository_impl.dart

Build docs developers (and LLMs) love