Infrastructure Layer
The infrastructure layer contains the implementation of the data sources and external dependencies of the feature. The infrastructure layer is responsible for fetching data from external sources, such as APIs or databases, and providing it to the application layer.
Getting started
Create a new folder named infrastructure
inside the feature folder.
Write the repository implementation
We'll be using firestore database to store the scratch notes. Create a new file named scratch_note_repository_impl.dart
inside the infrastructure
folder. This file will contain the implementation of the IScratchNoteRepository
interface.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fpdart/fpdart.dart';
import 'package:techshoi_app/features/core/domain/entities/failure.dart';
import 'package:techshoi_app/features/core/infrastructure/firebase_helpers.dart';
import 'package:techshoi_app/features/core/infrastructure/logger/techshoi_logger.dart';
import 'package:techshoi_app/features/scratch_notes/domain/i_scratch_note_repository.dart';
import 'package:techshoi_app/features/scratch_notes/domain/scratch_note.dart';
import 'package:uuid/uuid.dart';
class FirebaseScratchNoteRepository implements IScratchNoteRepository {
FirebaseScratchNoteRepository(this._firestore);
final FirebaseFirestore _firestore;
final scratchNotesCollectionName = 'scratch_notes';
@override
Future<Either<Failure, Unit>> addScratchNote(String title) async {
try {
final userDoc = await _firestore.userDocument();
final notesCollection = userDoc.collection(scratchNotesCollectionName);
final id = const Uuid().v4();
final scratchNote = ScratchNote(
id: id,
userId: userDoc.id,
title: title,
createdAt: DateTime.now(),
);
await notesCollection.doc(id).set(scratchNote.toJson());
return right(unit);
} catch (e) {
TechshoiLogger.instance.e('Failed to add scratch note. Error: $e');
return left(const Failure(message: 'Failed to add scratch note'));
}
}
@override
Future<Either<Failure, Unit>> deleteScratchNote(String noteId) async {
try {
final userDoc = await _firestore.userDocument();
final notesCollection = userDoc.collection(scratchNotesCollectionName);
await notesCollection.doc(noteId).delete();
return right(unit);
} catch (e) {
TechshoiLogger.instance.e('Failed to delete scratch note. Error: $e');
return left(const Failure(message: 'Failed to delete scratch note'));
}
}
@override
Future<Either<Failure, List<ScratchNote>>> getScratchNotes() async {
try {
final userDoc = await _firestore.userDocument();
final notesCollection = userDoc.collection(scratchNotesCollectionName);
final notesSnapshot = await notesCollection.get();
final notes = notesSnapshot.docs
.map((doc) => ScratchNote.fromJson(doc.data()))
.toList();
return right(notes);
} catch (e) {
TechshoiLogger.instance.e('Failed to get scratch notes. Error: $e');
return left(const Failure(message: 'Failed to get scratch notes'));
}
}
@override
Stream<Either<Failure, List<ScratchNote>>> listenToScratchNotes() async* {
final userDoc = await _firestore.userDocument();
final notesCollection = userDoc.collection(scratchNotesCollectionName);
yield* notesCollection.snapshots().map((snapshot) {
final notes =
snapshot.docs.map((doc) => ScratchNote.fromJson(doc.data())).toList();
return right(notes);
});
}
@override
Future<Either<Failure, Unit>> updateScratchNote(ScratchNote note) async {
try {
final userDoc = await _firestore.userDocument();
final notesCollection = userDoc.collection(scratchNotesCollectionName);
await notesCollection.doc(note.id).update(note.toJson());
return right(unit);
} catch (e) {
TechshoiLogger.instance.e('Failed to update scratch note. Error: $e');
return left(const Failure(message: 'Failed to update scratch note'));
}
}
}
In this implementation, we are using the cloud_firestore
package to interact with the Firestore database. We define a class FirebaseScratchNoteRepository
that implements the IScratchNoteRepository
interface. The methods in this class interact with the Firestore database to perform CRUD operations on scratch notes. We also use the fpdart
package to return Either
types to handle errors.
We're also using the .userDocument()
extension method from the firebase_helpers.dart
file to get the user document from Firestore. This method is defined in the core
module.
Next, create a file named configure_notes_dependencies.dart
inside the infrastructure
folder. This file will contain the setup for the infrastructure layer.
import 'package:techshoi_app/configure_dependencies.dart';
import 'package:techshoi_app/features/scratch_notes/application/scratch_note_cubit.dart';
import 'package:techshoi_app/features/scratch_notes/domain/i_scratch_note_repository.dart';
import 'package:techshoi_app/features/scratch_notes/infrastructure/firebase_scratch_note_repository.dart';
Future<void> configureNotesDependencies() async {
serviceLocator
..registerLazySingleton<IScratchNoteRepository>(
() => FirebaseScratchNoteRepository(
serviceLocator(),
),
)
..registerFactory<ScratchNoteCubit>(
() => ScratchNoteCubit(
serviceLocator(),
),
);
}
Then add this line to the configure_dependencies.dart
file to include the infrastructure layer setup:
import 'package:techshoi_app/features/scratch_notes/infrastructure/configure_notes_dependencies.dart';
Future<void> configureDependencies() async {
...
await configureNotesDependencies(); // Add this line
}
This will ensure that the infrastructure layer is set up when the app starts.