Managing Bookmark Lifecycle
Manage the lifecycle of bookmarks—including creation, partial updates, and state transitions like archiving or trashing—using the BookmarkService facade.
Creating a New Bookmark
To create a bookmark, use the create_bookmark method. This method validates the input data, persists the entity to the repository, indexes it for search, and invalidates any relevant cache entries.
from app.services.bookmark_service import BookmarkService
service = BookmarkService()
data = {
"url": "https://example.com",
"title": "Example Domain",
"description": "A domain used for illustrative examples"
}
bookmark, error = service.create_bookmark(data)
if error:
# Handle validation errors (e.g., invalid URL or empty title)
print(f"Failed to create bookmark: {error}")
else:
print(f"Created bookmark with ID: {bookmark.id}")
The create_bookmark method returns a tuple of (Optional[Bookmark], Optional[str]). If validation fails (via internal helpers like _validate_url), the first element is None and the second contains the error message.
Updating Bookmark Metadata
The update_bookmark method performs partial updates. It only modifies the fields provided in the data dictionary and automatically updates the updated_at timestamp via the _touch() method.
update_data = {
"title": "Updated Example Title",
"description": "An updated description for this bookmark."
}
bookmark, error = service.update_bookmark("bookmark_id_123", update_data)
if error:
print(f"Update failed: {error}")
elif not bookmark:
print("Bookmark not found")
else:
print(f"Successfully updated: {bookmark.title}")
Transitioning Bookmark States
Bookmarks in this system follow a specific lifecycle defined by the BookmarkStatus enum: ACTIVE, ARCHIVED, and TRASHED. The BookmarkService provides dedicated methods for these transitions.
Archiving a Bookmark
Archiving moves a bookmark out of the primary view without deleting it.
archived_bookmark = service.archive_bookmark("bookmark_id_123")
if archived_bookmark:
print(f"Status is now: {archived_bookmark.status.value}") # "archived"
Soft-Deleting (Trashing) a Bookmark
The delete_bookmark method performs a soft-delete. It does not remove the record from the database; instead, it transitions the status to TRASHED.
success = service.delete_bookmark("bookmark_id_123")
if not success:
print("Bookmark not found")
Restoring a Bookmark
You can restore a bookmark from either the ARCHIVED or TRASHED state back to ACTIVE.
restored_bookmark = service.restore_bookmark("bookmark_id_123")
if restored_bookmark:
print(f"Status restored to: {restored_bookmark.status.value}") # "active"
Listing Bookmarks by Status
When retrieving bookmarks, you can filter the results by their lifecycle state using the status parameter in list_bookmarks.
# Fetch only archived bookmarks
archived_list, total = service.list_bookmarks(page=1, per_page=10, status="archived")
# Fetch only trashed bookmarks
trash_list, total = service.list_bookmarks(page=1, per_page=10, status="trashed")
# Fetch active bookmarks (default behavior if status is not provided depends on repository implementation)
active_list, total = service.list_bookmarks(page=1, per_page=25, status="active")
Implementation Details
Singleton Pattern
BookmarkService is implemented as a singleton. You should instantiate it directly in your modules; it will share the same BookmarkRepository, LRUCache, and SearchIndex instances across the entire application.
# In app/routes/bookmarks.py
_service = BookmarkService()
Validation and Errors
Validation is handled within the service layer before any persistence occurs. Methods that modify data return a (result, error) tuple. Always check the error string to handle cases like:
- Invalid URL formats.
- Missing or empty titles.
- Invalid tag names (when managing tags through the service).
Cache Invalidation
The service automatically manages the LRUCache. Any operation that modifies a bookmark (update_bookmark, archive_bookmark, delete_bookmark, etc.) calls self._cache.invalidate(bookmark_id) to ensure subsequent retrievals fetch the fresh state from the repository.