ะŸะตั€ะตะนั‚ะธ ะดะพ ะทะผั–ัั‚ัƒ

7. Factory & Object Creation Lifecycle (ะคะฐะฑั€ะธะบะธ ั‚ะฐ ะถะธั‚ั‚ั”ะฒะธะน ั†ะธะบะป)

๐Ÿ“‹ ะ—ะผั–ัั‚

  1. ะžะณะปัะด ั„ะฐะฑั€ะธะบ ั‚ะฐ DI
  2. Driver Factory
  3. Storage Factory
  4. Registry Pattern
  5. DependencyRegistry (Singleton)
  6. ApplicationContainer (DI)
  7. ะ–ะธั‚ั‚ั”ะฒะธะน ั†ะธะบะป ะพะฑ'ั”ะบั‚ั–ะฒ
  8. Transient Objects

ะžะณะปัะด ั„ะฐะฑั€ะธะบ ั‚ะฐ DI

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    FACTORY & DI ARCHITECTURE                                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                         FACTORIES (OCP)                              โ”‚    โ”‚
โ”‚  โ”‚                                                                      โ”‚    โ”‚
โ”‚  โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚   DriverFactory     โ”‚    โ”‚   StorageFactory    โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                     โ”‚    โ”‚                     โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  _DRIVER_REGISTRY   โ”‚    โ”‚  _STORAGE_REGISTRY  โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  {                  โ”‚    โ”‚  {                  โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚    "http": factory, โ”‚    โ”‚    "memory": factoryโ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚    "async": factory,โ”‚    โ”‚    "json": factory, โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚    "playwright": ..โ”‚    โ”‚    "sqlite": factoryโ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  }                  โ”‚    โ”‚  }                  โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                     โ”‚    โ”‚                     โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  register_driver()  โ”‚    โ”‚  register_storage() โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  create_driver()    โ”‚    โ”‚  create_storage()   โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                    DOMAIN REGISTRIES (OCP)                           โ”‚    โ”‚
โ”‚  โ”‚                                                                      โ”‚    โ”‚
โ”‚  โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚ CrawlModeRegistry   โ”‚    โ”‚ MergeStrategyRegistryโ”‚               โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                     โ”‚    โ”‚                     โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚ "sequential"        โ”‚    โ”‚ "first"             โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚ "multiprocessing"   โ”‚    โ”‚ "last"              โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚ "celery"            โ”‚    โ”‚ "merge"             โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚ [custom]            โ”‚    โ”‚ "newest"            โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                     โ”‚    โ”‚ "oldest"            โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                     โ”‚    โ”‚ "custom"            โ”‚                โ”‚    โ”‚
โ”‚  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚    โ”‚
โ”‚  โ”‚                                                                      โ”‚    โ”‚
โ”‚  โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚           ChangeDetectionStrategyRegistry                    โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                                                              โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  "hash"     - SHA256 ะฒั–ะด text_content                        โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  "metadata" - ะฟะพั€ั–ะฒะฝัะฝะฝั metadata ะฟะพะปั–ะฒ                      โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  [custom]   - ะบะฐัั‚ะพะผะฝะฐ ัั‚ั€ะฐั‚ะตะณั–ั                             โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚              DEPENDENCY INJECTION (DI)                               โ”‚    โ”‚
โ”‚  โ”‚                                                                      โ”‚    โ”‚
โ”‚  โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚           DependencyRegistry (Singleton)                     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                                                              โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข plugin_manager_factory                                    โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข tree_parser_factory                                       โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข hash_strategy_factory                                     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข node_class                                                โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข edge_class                                                โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข default_merge_strategy                                    โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                                                              โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  Thread-safe Singleton ะท lazy initialization                 โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ”‚                                                                      โ”‚    โ”‚
โ”‚  โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚           ApplicationContainer                               โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚                                                              โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  ะžะฑ'ั”ะดะฝัƒั” ะฒัั– ะทะฐะปะตะถะฝะพัั‚ั– ะดะปั Spider:                         โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข driver (IDriver)                                          โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข storage (IStorage)                                        โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข scheduler (Scheduler)                                     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข event_bus (EventBus)                                      โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข plugins (List[BaseNodePlugin])                            โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ”‚  โ€ข config (CrawlerConfig)                                    โ”‚   โ”‚    โ”‚
โ”‚  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Driver Factory

ะ ะพะทั‚ะฐัˆัƒะฒะฐะฝะฝั

ะคะฐะนะป: graph_crawler/application/services/driver_factory.py

ะั€ั…ั–ั‚ะตะบั‚ัƒั€ะฐ

# ะ’ะฝัƒั‚ั€ั–ัˆะฝั–ะน Registry (Dict)
_DRIVER_REGISTRY: Dict[str, DriverFactory] = {}

# ะขะธะฟ ั„ะฐะฑั€ะธะบะธ
DriverFactory = Callable[[dict], IDriver]

API

# ะ ะตั”ัั‚ั€ะฐั†ั–ั ะฝะพะฒะพะณะพ ะดั€ะฐะนะฒะตั€ะฐ
def register_driver(name: str, factory: DriverFactory) -> None:
    """
    ะ ะตั”ัั‚ั€ัƒั” ะฝะพะฒะธะน ั‚ะธะฟ ะดั€ะฐะนะฒะตั€ะฐ (OCP).

    Args:
        name: ะะฐะทะฒะฐ ะดั€ะฐะนะฒะตั€ะฐ (lowercase)
        factory: ะคัƒะฝะบั†ั–ั-ั„ะฐะฑั€ะธะบะฐ ัะบะฐ ะฟั€ะธะนะผะฐั” config ั– ะฟะพะฒะตั€ั‚ะฐั” IDriver
    """

# ะกั‚ะฒะพั€ะตะฝะฝั ะดั€ะฐะนะฒะตั€ะฐ
def create_driver(
    driver: DriverType = None,
    config: Optional[dict] = None
) -> IDriver:
    """
    ะกั‚ะฒะพั€ัŽั” ะดั€ะฐะนะฒะตั€ ะท string ะฐะฑะพ ะฟะพะฒะตั€ั‚ะฐั” instance.

    Args:
        driver: "http" | "async" | "playwright" | "stealth" | IDriver instance
        config: ะšะพะฝั„ั–ะณัƒั€ะฐั†ั–ั ะดั€ะฐะนะฒะตั€ะฐ
    """

# ะกะฟะธัะพะบ ะดะพัั‚ัƒะฟะฝะธั… ะดั€ะฐะนะฒะตั€ั–ะฒ
def get_available_drivers() -> list[str]:
    """ะŸะพะฒะตั€ั‚ะฐั” ัะฟะธัะพะบ ะทะฐั€ะตั”ัั‚ั€ะพะฒะฐะฝะธั… ะดั€ะฐะนะฒะตั€ั–ะฒ."""

ะ’ะฑัƒะดะพะฒะฐะฝั– ะดั€ะฐะนะฒะตั€ะธ

ะะฐะทะฒะฐ ะšะปะฐั ะ‘ั–ะฑะปั–ะพั‚ะตะบะฐ ะžะฟะธั
http HTTPDriver requests ะกะธะฝั…ั€ะพะฝะฝะธะน HTTP (default)
async AsyncDriver aiohttp ะัะธะฝั…ั€ะพะฝะฝะธะน HTTP
playwright PlaywrightDriver playwright JS rendering
stealth StealthDriver playwright Anti-bot bypass

ะ†ะฝั–ั†ั–ะฐะปั–ะทะฐั†ั–ั

def _register_builtin_drivers():
    """ะ ะตั”ัั‚ั€ัƒั” ะฒะฑัƒะดะพะฒะฐะฝั– ะดั€ะฐะนะฒะตั€ะธ ะฟั€ะธ ั–ะผะฟะพั€ั‚ั– ะผะพะดัƒะปั."""

    def http_factory(config: dict) -> IDriver:
        from graph_crawler.infrastructure.transport import HTTPDriver
        return HTTPDriver(config)

    def async_factory(config: dict) -> IDriver:
        from graph_crawler.infrastructure.transport.async_http import AsyncDriver
        return AsyncDriver(config)

    def playwright_factory(config: dict) -> IDriver:
        from graph_crawler.infrastructure.transport.playwright import PlaywrightDriver
        return PlaywrightDriver(config)

    def stealth_factory(config: dict) -> IDriver:
        from graph_crawler.infrastructure.transport.stealth_driver import StealthDriver
        return StealthDriver(config)

    _DRIVER_REGISTRY["http"] = http_factory
    _DRIVER_REGISTRY["async"] = async_factory
    _DRIVER_REGISTRY["playwright"] = playwright_factory
    _DRIVER_REGISTRY["stealth"] = stealth_factory

# ะะฒั‚ะพะผะฐั‚ะธั‡ะฝะฐ ั–ะฝั–ั†ั–ะฐะปั–ะทะฐั†ั–ั ะฟั€ะธ ั–ะผะฟะพั€ั‚ั–
_register_builtin_drivers()

Storage Factory

ะ ะพะทั‚ะฐัˆัƒะฒะฐะฝะฝั

ะคะฐะนะป: graph_crawler/application/services/storage_factory.py

API

# ะ ะตั”ัั‚ั€ะฐั†ั–ั
def register_storage(name: str, factory: Callable[[dict], IStorage]) -> None:
    """ะ ะตั”ัั‚ั€ัƒั” storage factory (OCP)."""

# ะกั‚ะฒะพั€ะตะฝะฝั
def create_storage(
    storage: StorageType = None,
    config: Optional[Dict] = None,
    **kwargs
) -> IStorage:
    """ะกั‚ะฒะพั€ัŽั” storage ะท Registry Pattern."""

# ะกะฟะธัะพะบ ั‚ะธะฟั–ะฒ
def get_available_storage_types() -> list:
    """ะŸะพะฒะตั€ั‚ะฐั” ัะฟะธัะพะบ ะดะพัั‚ัƒะฟะฝะธั… storage ั‚ะธะฟั–ะฒ."""

ะ’ะฑัƒะดะพะฒะฐะฝั– storage

ะะฐะทะฒะฐ ะšะปะฐั ะ ะตะบะพะผะตะฝะดะพะฒะฐะฝะพ ะดะปั ะ‘ั–ะฑะปั–ะพั‚ะตะบะฐ
memory MemoryStorage < 1K nodes built-in
json JSONStorage 1K - 10K nodes aiofiles
sqlite SQLiteStorage 10K - 100K nodes aiosqlite
postgresql PostgreSQLStorage 100K+ nodes asyncpg
mongodb MongoDBStorage 100K+ nodes motor

Registry Pattern

BaseRegistry

ะคะฐะนะป: graph_crawler/domain/entities/registries.py

class BaseRegistry(ABC):
    """ะ‘ะฐะทะพะฒะธะน ะบะปะฐั ะดะปั ะฒัั–ั… ั€ะตั”ัั‚ั€ั–ะฒ."""

    _registry: Dict[str, Any] = {}

    @classmethod
    def register(cls, name: str, item: Any) -> None:
        """ะ ะตั”ัั‚ั€ัƒั” ะตะปะตะผะตะฝั‚."""

    @classmethod
    def unregister(cls, name: str) -> None:
        """ะ’ะธะดะฐะปัั” ะตะปะตะผะตะฝั‚."""

    @classmethod
    def get(cls, name: str) -> Optional[Any]:
        """ะžั‚ั€ะธะผัƒั” ะตะปะตะผะตะฝั‚."""

    @classmethod
    def get_all_names(cls) -> List[str]:
        """ะกะฟะธัะพะบ ะฒัั–ั… ะฝะฐะทะฒ."""

    @classmethod
    def is_registered(cls, name: str) -> bool:
        """ะŸะตั€ะตะฒั–ั€ัั” ั€ะตั”ัั‚ั€ะฐั†ั–ัŽ."""

    @classmethod
    def clear(cls) -> None:
        """ะžั‡ะธั‰ัƒั” ั€ะตั”ัั‚ั€ (ะดะปั ั‚ะตัั‚ั–ะฒ)."""

ะšะพะฝะบั€ะตั‚ะฝั– Registry

class CrawlModeRegistry(BaseRegistry):
    """
    ะ ะตั”ัั‚ั€ ั€ะตะถะธะผั–ะฒ ะบั€ะฐัƒะปั–ะฝะณัƒ.

    ะ”ะตั„ะพะปั‚ะฝั– ั€ะตะถะธะผะธ:
    - sequential: GraphSpider
    - multiprocessing: MultiprocessingSpider
    - celery: CeleryBatchSpider
    """

class MergeStrategyRegistry(BaseRegistry):
    """
    ะ ะตั”ัั‚ั€ ัั‚ั€ะฐั‚ะตะณั–ะน merge ะดะปั Graph.union().

    ะ”ะตั„ะพะปั‚ะฝั– ัั‚ั€ะฐั‚ะตะณั–ั—:
    - first, last, merge, newest, oldest, custom
    """

class ChangeDetectionStrategyRegistry(BaseRegistry):
    """
    ะ ะตั”ัั‚ั€ ัั‚ั€ะฐั‚ะตะณั–ะน ะดะตั‚ะตะบั†ั–ั— ะทะผั–ะฝ.

    ะ”ะตั„ะพะปั‚ะฝั– ัั‚ั€ะฐั‚ะตะณั–ั—:
    - hash: SHA256 ะฒั–ะด text_content
    - metadata: ะฟะพั€ั–ะฒะฝัะฝะฝั metadata ะฟะพะปั–ะฒ
    """

Lazy Factory Pattern

ะ”ะปั ัƒะฝะธะบะฝะตะฝะฝั circular imports ะฒะธะบะพั€ะธัั‚ะพะฒัƒั”ั‚ัŒัั lazy factory:

def _lazy_import_spider(mode: str):
    """Lazy factory ะดะปั ั–ะผะฟะพั€ั‚ัƒ Spider ะบะปะฐัั–ะฒ."""

    def factory():
        if mode == "sequential":
            from graph_crawler.application.use_cases.crawling.spider import GraphSpider
            return GraphSpider
        elif mode == "multiprocessing":
            from graph_crawler.application.use_cases.crawling.multiprocessing_spider import (
                MultiprocessingSpider,
            )
            return MultiprocessingSpider
        elif mode == "celery":
            from graph_crawler.application.use_cases.crawling.celery_batch_spider import (
                CeleryBatchSpider,
            )
            return CeleryBatchSpider
        else:
            raise ValueError(f"Unknown crawl mode: {mode}")

    return factory

# ะ ะตั”ัั‚ั€ะฐั†ั–ั ะท lazy factory
CrawlModeRegistry.register("sequential", _lazy_import_spider("sequential"))

DependencyRegistry

ะŸั€ะธะทะฝะฐั‡ะตะฝะฝั

ะคะฐะนะป: graph_crawler/application/context/dependency_registry.py

Thread-safe Singleton ะดะปั ัƒะฟั€ะฐะฒะปั–ะฝะฝั ะทะฐะปะตะถะฝะพัั‚ัะผะธ, ัะบั– ะฝะต ัะตั€ั–ะฐะปั–ะทัƒัŽั‚ัŒัั: - plugin_manager - ัƒะฟั€ะฐะฒะปัั” ะฟะปะฐะณั–ะฝะฐะผะธ - tree_parser - ะฟะฐั€ัะตั€ HTML - hash_strategy - ัั‚ั€ะฐั‚ะตะณั–ั ะพะฑั‡ะธัะปะตะฝะฝั hash

ะŸั€ะพะฑะปะตะผะฐ

# Node ะผะฐั” ะฟะพะปั ัะบั– ะะ• ัะตั€ั–ะฐะปั–ะทัƒัŽั‚ัŒัั:
class Node(BaseModel):
    plugin_manager: Optional[Any] = Field(default=None, exclude=True)
    tree_parser: Optional[Any] = Field(default=None, exclude=True)
    hash_strategy: Optional[Any] = Field(default=None, exclude=True)

ะŸั–ัะปั ะดะตัะตั€ั–ะฐะปั–ะทะฐั†ั–ั— ะท JSON/SQLite ั†ั– ะฟะพะปั ะฑัƒะดัƒั‚ัŒ None.

ะ ั–ัˆะตะฝะฝั

from graph_crawler.application.context import DependencyRegistry

# 1. ะšะพะฝั„ั–ะณัƒั€ะฐั†ั–ั ะฟั€ะธ ัั‚ะฐั€ั‚ั– ะฟั€ะพะณั€ะฐะผะธ
DependencyRegistry.configure(
    plugin_manager_factory=lambda: NodePluginManager(),
    tree_parser_factory=lambda: BeautifulSoupAdapter(),
    hash_strategy_factory=lambda: DefaultHashStrategy(),
    default_merge_strategy='merge'
)

# 2. ะžั‚ั€ะธะผะฐะฝะฝั ะบะพะฝั‚ะตะบัั‚ัƒ ะดะปั ะดะตัะตั€ั–ะฐะปั–ะทะฐั†ั–ั—
context = DependencyRegistry.get_context()
graph = GraphMapper.to_domain(graph_dto, context=context)

# 3. Override ะดะปั ะบะพะฝะบั€ะตั‚ะฝะพะณะพ ะฒะธะฟะฐะดะบัƒ
context = DependencyRegistry.get_context(
    plugin_manager=custom_pm,  # Override ั‚ั–ะปัŒะบะธ plugin_manager
)

API

class DependencyRegistry:
    """Thread-safe Singleton."""

    @classmethod
    def configure(
        cls,
        plugin_manager: Optional[Any] = None,
        plugin_manager_factory: Optional[Callable] = None,
        tree_parser: Optional[Any] = None,
        tree_parser_factory: Optional[Callable] = None,
        hash_strategy: Optional[Any] = None,
        hash_strategy_factory: Optional[Callable] = None,
        node_class: Optional[Type] = None,
        edge_class: Optional[Type] = None,
        default_merge_strategy: str = "last",
    ) -> None:
        """ะšะพะฝั„ั–ะณัƒั€ัƒั” ะดะตั„ะพะปั‚ะฝั– ะทะฐะปะตะถะฝะพัั‚ั–."""

    @classmethod
    def get_context(
        cls,
        plugin_manager: Optional[Any] = None,
        tree_parser: Optional[Any] = None,
        hash_strategy: Optional[Any] = None,
        node_class: Optional[Type] = None,
        edge_class: Optional[Type] = None,
        default_merge_strategy: Optional[str] = None,
    ) -> Dict[str, Any]:
        """ะžั‚ั€ะธะผัƒั” ะบะพะฝั‚ะตะบัั‚ ะท ะผะพะถะปะธะฒั–ัั‚ัŽ override."""

    @classmethod
    def reset(cls) -> None:
        """ะกะบะธะดะฐั” ะดะพ ะดะตั„ะพะปั‚ั–ะฒ (ะดะปั ั‚ะตัั‚ั–ะฒ)."""

    # Shortcut ะผะตั‚ะพะดะธ
    @classmethod
    def set_plugin_manager(cls, pm: Any) -> None: ...
    @classmethod
    def get_plugin_manager(cls) -> Optional[Any]: ...
    @classmethod
    def set_tree_parser(cls, tp: Any) -> None: ...
    @classmethod
    def get_tree_parser(cls) -> Optional[Any]: ...
    @classmethod
    def set_default_merge_strategy(cls, strategy: str) -> None: ...
    @classmethod
    def get_default_merge_strategy(cls) -> str: ...

ApplicationContainer

ะŸั€ะธะทะฝะฐั‡ะตะฝะฝั

ะคะฐะนะป: graph_crawler/application/services/application_container.py

DI ะบะพะฝั‚ะตะนะฝะตั€ ัะบะธะน ะพะฑ'ั”ะดะฝัƒั” ะฒัั– ะทะฐะปะตะถะฝะพัั‚ั– ะดะปั Spider.

ะกั‚ั€ัƒะบั‚ัƒั€ะฐ

from dataclasses import dataclass
from typing import List, Optional

@dataclass
class ApplicationContainer:
    """
    DI ะบะพะฝั‚ะตะนะฝะตั€ ะดะปั Spider.

    ะžะฑ'ั”ะดะฝัƒั” ะฒัั– ะทะฐะปะตะถะฝะพัั‚ั– ะฒ ะพะดะฝะพะผัƒ ะผั–ัั†ั–.
    Spider ะพั‚ั€ะธะผัƒั” ั†ะตะน ะบะพะฝั‚ะตะนะฝะตั€ ั– ะฒะธะบะพั€ะธัั‚ะพะฒัƒั” ะทะฐะปะตะถะฝะพัั‚ั–.
    """

    # ะžัะฝะพะฒะฝั– ะทะฐะปะตะถะฝะพัั‚ั–
    driver: IDriver
    storage: IStorage
    scheduler: Scheduler
    event_bus: EventBus

    # ะŸะปะฐะณั–ะฝะธ ั‚ะฐ middleware
    plugins: List[BaseNodePlugin]
    middleware_chain: MiddlewareChain

    # ะšะพะฝั„ั–ะณัƒั€ะฐั†ั–ั
    config: CrawlerConfig

    # ะšะฐัั‚ะพะผะฝั– ะบะปะฐัะธ
    node_class: type = Node
    edge_class: type = Edge

    # Factories
    node_factory: Optional[Callable] = None
    edge_factory: Optional[Callable] = None

ะกั‚ะฒะพั€ะตะฝะฝั ะบะพะฝั‚ะตะนะฝะตั€ะฐ

# ะ’ัะตั€ะตะดะธะฝั– API Layer (crawl() ั„ัƒะฝะบั†ั–ั)
def _create_container(config: CrawlerConfig) -> ApplicationContainer:
    """
    ะกั‚ะฒะพั€ัŽั” DI ะบะพะฝั‚ะตะนะฝะตั€ ะท ะบะพะฝั„ั–ะณัƒั€ะฐั†ั–ั—.

    ะ’ะธะบะพั€ะธัั‚ะพะฒัƒั” ั„ะฐะฑั€ะธะบะธ ะดะปั ัั‚ะฒะพั€ะตะฝะฝั ะบะพะผะฟะพะฝะตะฝั‚ั–ะฒ.
    """

    # ะกั‚ะฒะพั€ัŽั”ะผะพ driver ั‡ะตั€ะตะท factory
    driver = create_driver(
        config.driver,
        config.driver_config
    )

    # ะกั‚ะฒะพั€ัŽั”ะผะพ storage ั‡ะตั€ะตะท factory
    storage = create_storage(
        config.storage,
        config.storage_config
    )

    # ะกั‚ะฒะพั€ัŽั”ะผะพ event bus
    event_bus = EventBus()

    # ะกั‚ะฒะพั€ัŽั”ะผะพ scheduler
    scheduler = Scheduler(
        url_rules=config.url_rules,
        max_depth=config.max_depth
    )

    # ะกั‚ะฒะพั€ัŽั”ะผะพ plugin manager ะท ะฟะปะฐะณั–ะฝะฐะผะธ
    plugins = config.plugins or get_default_node_plugins()

    return ApplicationContainer(
        driver=driver,
        storage=storage,
        scheduler=scheduler,
        event_bus=event_bus,
        plugins=plugins,
        middleware_chain=MiddlewareChain(),
        config=config,
        node_class=config.node_class or Node,
        edge_class=config.edge_class or Edge,
    )

ะ–ะธั‚ั‚ั”ะฒะธะน ั†ะธะบะป ะพะฑ'ั”ะบั‚ั–ะฒ

Node Lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                           NODE LIFECYCLE                                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚  โ”‚ ะ•ะขะะŸ 1: URL_STAGE (ะกั‚ะฒะพั€ะตะฝะฝั)                                 โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ 1. Node.__init__(url="...")                                   โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 2. model_post_init()                                          โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 3. _trigger_node_created_hook()                               โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 4. ON_NODE_CREATED plugins (sync)                             โ”‚          โ”‚
โ”‚  โ”‚    โ€ข ะะฝะฐะปั–ะท URL                                               โ”‚          โ”‚
โ”‚  โ”‚    โ€ข ะ’ัั‚ะฐะฝะพะฒะปะตะฝะฝั should_scan, can_create_edges               โ”‚          โ”‚
โ”‚  โ”‚    โ€ข ะ’ัั‚ะฐะฝะพะฒะปะตะฝะฝั priority                                    โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ ะ”ะพัั‚ัƒะฟะฝะพ: url, depth, should_scan, can_create_edges           โ”‚          โ”‚
โ”‚  โ”‚ lifecycle_stage = URL_STAGE                                   โ”‚          โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
โ”‚                              โ”‚                                              โ”‚
โ”‚                              โ”‚ (ัะบั‰ะพ should_scan == True)                   โ”‚
โ”‚                              โ–ผ                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚  โ”‚ ะ•ะขะะŸ 2: HTML_STAGE (ะžะฑั€ะพะฑะบะฐ HTML)                             โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ 5. await node.process_html(html)                              โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 6. _parse_html(html) โ†’ (parser, html_tree)                    โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 7. _execute_plugins() (async)                                 โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ ON_BEFORE_SCAN plugins                                โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ ON_HTML_PARSED plugins                                โ”‚          โ”‚
โ”‚  โ”‚    โ”‚    โ€ข MetadataExtractor โ†’ metadata                        โ”‚          โ”‚
โ”‚  โ”‚    โ”‚    โ€ข LinkExtractor โ†’ extracted_links                     โ”‚          โ”‚
โ”‚  โ”‚    โ”‚    โ€ข PhoneExtractor โ†’ user_data['phones']                โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ _update_from_context()                                โ”‚          โ”‚
โ”‚  โ”‚    โ”‚    โ€ข metadata = context.metadata                         โ”‚          โ”‚
โ”‚  โ”‚    โ”‚    โ€ข user_data.update(context.user_data)                 โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ””โ”€โ”€โ–ถ ON_AFTER_SCAN plugins                                 โ”‚          โ”‚
โ”‚  โ”‚         โ€ข Vectorization                                       โ”‚          โ”‚
โ”‚  โ”‚         โ€ข ML analysis                                         โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 8. _compute_content_hash()                                    โ”‚          โ”‚
โ”‚  โ”‚    โ”‚                                                          โ”‚          โ”‚
โ”‚  โ”‚    โ–ผ                                                          โ”‚          โ”‚
โ”‚  โ”‚ 9. _cleanup_memory()                                          โ”‚          โ”‚
โ”‚  โ”‚    โ€ข del html                                                 โ”‚          โ”‚
โ”‚  โ”‚    โ€ข del html_tree                                            โ”‚          โ”‚
โ”‚  โ”‚    โ€ข context.html = None                                      โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ lifecycle_stage = HTML_STAGE                                  โ”‚          โ”‚
โ”‚  โ”‚ ะ”ะพัั‚ัƒะฟะฝะพ: metadata, user_data, content_hash                   โ”‚          โ”‚
โ”‚  โ”‚ HTML ะ’ะ˜ะ”ะะ›ะ•ะะž ะท ะฟะฐะผ'ัั‚ั–!                                      โ”‚          โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
โ”‚                              โ”‚                                              โ”‚
โ”‚                              โ–ผ                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚  โ”‚ ะ—ะ‘ะ•ะ ะ†ะ“ะะะะฏ / ะ’ะ†ะ”ะะžะ’ะ›ะ•ะะะฏ                                      โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ 10. storage.save_graph(graph)                                 โ”‚          โ”‚
โ”‚  โ”‚     โ€ข node.model_dump() โ†’ JSON                                โ”‚          โ”‚
โ”‚  โ”‚     โ€ข plugin_manager, tree_parser EXCLUDED                    โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ”‚ 11. storage.load_graph()                                      โ”‚          โ”‚
โ”‚  โ”‚     โ€ข Node.model_validate(data, context=context)              โ”‚          โ”‚
โ”‚  โ”‚     โ€ข node.restore_dependencies(pm, tp, hs)                   โ”‚          โ”‚
โ”‚  โ”‚                                                               โ”‚          โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
โ”‚                                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Spider Lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                          SPIDER LIFECYCLE                                    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                             โ”‚
โ”‚  1. ะกะขะ’ะžะ ะ•ะะะฏ                                                               โ”‚
โ”‚     spider = GraphSpider(container)                                         โ”‚
โ”‚                                                                             โ”‚
โ”‚  2. ะ†ะะ†ะฆะ†ะะ›ะ†ะ—ะะฆะ†ะฏ                                                           โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ scheduler.add_url(start_url, depth=0)                              โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ event_bus.publish(CRAWL_STARTED)                                   โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ””โ”€โ”€โ–ถ plugin_manager.setup() ะดะปั ะบะพะถะฝะพะณะพ ะฟะปะฐะณั–ะฝะฐ                         โ”‚
โ”‚                                                                             โ”‚
โ”‚  3. ะ“ะžะ›ะžะ’ะะ˜ะ™ ะฆะ˜ะšะ›                                                           โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”‚  while not scheduler.is_empty() and pages < max_pages:                โ”‚
โ”‚     โ”‚  โ”‚                                                                    โ”‚
โ”‚     โ”‚  โ”œโ”€โ”€โ–ถ url, depth = scheduler.get_next_url()                           โ”‚
โ”‚     โ”‚  โ”‚                                                                    โ”‚
โ”‚     โ”‚  โ”œโ”€โ”€โ–ถ node = Node(url, depth, plugin_manager)                         โ”‚
โ”‚     โ”‚  โ”‚    โ””โ”€โ”€โ–ถ ON_NODE_CREATED plugins                                    โ”‚
โ”‚     โ”‚  โ”‚                                                                    โ”‚
โ”‚     โ”‚  โ”œโ”€โ”€โ–ถ if node.should_scan:                                            โ”‚
โ”‚     โ”‚  โ”‚    โ”‚                                                               โ”‚
โ”‚     โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ response = await driver.fetch(url)                         โ”‚
โ”‚     โ”‚  โ”‚    โ”‚                                                               โ”‚
โ”‚     โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ links = await node.process_html(response.html)             โ”‚
โ”‚     โ”‚  โ”‚    โ”‚    โ””โ”€โ”€โ–ถ ON_BEFORE_SCAN, ON_HTML_PARSED, ON_AFTER_SCAN         โ”‚
โ”‚     โ”‚  โ”‚    โ”‚                                                               โ”‚
โ”‚     โ”‚  โ”‚    โ”œโ”€โ”€โ–ถ for link in links:                                         โ”‚
โ”‚     โ”‚  โ”‚    โ”‚    scheduler.add_url(link, depth+1)                           โ”‚
โ”‚     โ”‚  โ”‚    โ”‚                                                               โ”‚
โ”‚     โ”‚  โ”‚    โ””โ”€โ”€โ–ถ graph.add_node(node)                                       โ”‚
โ”‚     โ”‚  โ”‚                                                                    โ”‚
โ”‚     โ”‚  โ””โ”€โ”€โ–ถ event_bus.publish(NODE_SCANNED)                                 โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚  4. ะ—ะะ’ะ•ะ ะจะ•ะะะฏ                                                              โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ await storage.save_graph(graph)                                    โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ event_bus.publish(CRAWL_COMPLETED)                                 โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ plugin_manager.teardown() ะดะปั ะบะพะถะฝะพะณะพ ะฟะปะฐะณั–ะฝะฐ                      โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ”œโ”€โ”€โ–ถ await driver.close()                                               โ”‚
โ”‚     โ”‚                                                                       โ”‚
โ”‚     โ””โ”€โ”€โ–ถ await storage.close()                                              โ”‚
โ”‚                                                                             โ”‚
โ”‚  5. ะ ะ•ะ—ะฃะ›ะฌะขะะข                                                               โ”‚
โ”‚     return graph                                                            โ”‚
โ”‚                                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Transient Objects

ะฉะพ ัั‚ะฒะพั€ัŽั”ั‚ัŒัั ะฐะฒั‚ะพะผะฐั‚ะธั‡ะฝะพ

ะžะฑ'ั”ะบั‚ ะกั‚ะฒะพั€ัŽั”ั‚ัŒัั ะ—ะฝะธั‰ัƒั”ั‚ัŒัั ะŸั€ะธะผั–ั‚ะบะฐ
html driver.fetch() _cleanup_memory() ะะต ะทะฑะตั€ั–ะณะฐั”ั‚ัŒัั ะฒ Node
html_tree _parse_html() _cleanup_memory() BeautifulSoup ะดะตั€ะตะฒะพ
NodePluginContext process_html() ะฟั–ัะปั ะฒะธะบะพะฝะฐะฝะฝั ะŸะตั€ะตะดะฐั”ั‚ัŒัั ะผั–ะถ ะฟะปะฐะณั–ะฝะฐะผะธ
MiddlewareContext ะฟะตั€ะตะด fetch ะฟั–ัะปั fetch ะŸะตั€ะตะดะฐั”ั‚ัŒัั ะผั–ะถ middleware
FetchResponse driver.fetch() ะฟั–ัะปั process DTO ะดะปั ะฒั–ะดะฟะพะฒั–ะดั–
CrawlerEvent EventBus ะฟั–ัะปั notify ะ—ะฑะตั€ั–ะณะฐั”ั‚ัŒัั ะฒ history ัะบั‰ะพ enabled

ะฉะพ ะทะฑะตั€ั–ะณะฐั”ั‚ัŒัั

ะžะฑ'ั”ะบั‚ Scope ะŸั€ะธะผั–ั‚ะบะฐ
Node Graph lifetime metadata, user_data ะทะฑะตั€ั–ะณะฐัŽั‚ัŒัั
Edge Graph lifetime anchor_text, link_type
Graph Application nodes, edges, stats
EventBus.history Optional ะฏะบั‰ะพ enabled

Hidden Factories

# 1. Node Factory (ะฒะฝัƒั‚ั€ั–ัˆะฝั)
# ะ’ Spider ะฒะธะบะพั€ะธัั‚ะพะฒัƒั”ั‚ัŒัั:
node = container.node_class(
    url=url,
    depth=depth,
    plugin_manager=plugin_manager
)

# 2. Edge Factory (ะฒะฝัƒั‚ั€ั–ัˆะฝั)
# ะ’ LinkProcessor:
edge = container.edge_class(
    source_node_id=source.node_id,
    target_node_id=target.node_id,
    anchor_text=anchor,
    link_type=types
)

# 3. Plugin Context Factory
# ะ’ Node.process_html():
context = NodePluginContext(
    node=self,
    url=self.url,
    depth=self.depth,
    ...
)

๐Ÿ“Š ะ”ั–ะฐะณั€ะฐะผะฐ ะทะฐะปะตะถะฝะพัั‚ะตะน

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      DEPENDENCY FLOW                                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                             โ”‚
โ”‚  crawl(url, ...)                                                            โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ–ผ                                                                     โ”‚
โ”‚  _create_container()                                                        โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ”œโ”€โ”€โ–ถ DriverFactory.create_driver()                                    โ”‚
โ”‚       โ”‚         โ”‚                                                           โ”‚
โ”‚       โ”‚         โ””โ”€โ”€โ–ถ _DRIVER_REGISTRY["http"]() โ†’ HTTPDriver                โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ”œโ”€โ”€โ–ถ StorageFactory.create_storage()                                  โ”‚
โ”‚       โ”‚         โ”‚                                                           โ”‚
โ”‚       โ”‚         โ””โ”€โ”€โ–ถ _STORAGE_REGISTRY["memory"]() โ†’ MemoryStorage          โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ”œโ”€โ”€โ–ถ EventBus()                                                       โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ”œโ”€โ”€โ–ถ Scheduler(url_rules)                                             โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ””โ”€โ”€โ–ถ NodePluginManager(plugins)                                       โ”‚
โ”‚                   โ”‚                                                         โ”‚
โ”‚                   โ””โ”€โ”€โ–ถ get_default_node_plugins()                           โ”‚
โ”‚                             โ€ข MetadataExtractorPlugin                       โ”‚
โ”‚                             โ€ข LinkExtractorPlugin                           โ”‚
โ”‚                             โ€ข TextExtractorPlugin                           โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ–ผ                                                                     โ”‚
โ”‚  ApplicationContainer(driver, storage, scheduler, event_bus, plugins)       โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ–ผ                                                                     โ”‚
โ”‚  Spider(container)                                                          โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ–ผ                                                                     โ”‚
โ”‚  await spider.crawl(start_url)                                              โ”‚
โ”‚       โ”‚                                                                     โ”‚
โ”‚       โ–ผ                                                                     โ”‚
โ”‚  return Graph                                                               โ”‚
โ”‚                                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ”— ะะฐะฒั–ะณะฐั†ั–ั