A configurable, rule-driven Java engine that turns a messy B2B lead list into a clean, enriched, scored and segmented one — the automation behind a professional lead-research service.
flowchart LR
A[raw_leads.csv] --> B[Clean & validate<br/>names · emails · domains]
B --> C[Enrich<br/>region · email pattern]
C --> D[Score<br/>configurable rules engine]
D --> E[Segment A/B/C + dedupe]
E --> F[leads_processed.csv<br/>leads_output.json<br/>report.html]
- Configurable scoring — weights, segment thresholds and target regions live in
config/scoring.properties. Change the strategy without touching code. - Rules engine — each signal is a
ScoringRule; add a rule to extend scoring, the engine just sums them. Clean, testable OOP. - Real enrichment — normalizes countries to ISO/region, and derives a best-guess email pattern (
first.last@domain) when the email is missing. - Data hygiene — validates emails (invalid ones flagged, not silently kept), deduplicates (by email, else company+contact), and keeps the higher-quality record.
- Three outputs — clean CSV, JSON, and a standalone HTML report.
com.velat.leads
├── App entry point
├── model/ Lead · EngineConfig · Metrics
├── io/ CsvIo · JsonWriter
├── clean/ Normalizer · EmailValidator
├── enrich/ Enricher region · domain · email pattern
├── rules/ ScoringRule · Rules · ScoringEngine ← configurable scoring
├── pipeline/ LeadPipeline orchestration
└── report/ ReportWriter HTML summary
Zero runtime dependencies (pure JDK); JUnit 5 for tests.
mvn test # JUnit 5 suite
mvn -q exec:java # runs the engine on data/raw_leads.csv
# plain JDK:
# javac -d target/classes $(find src/main -name "*.java")
# java -cp target/classes com.velat.leads.App=== Lead Intelligence Engine ===
Input 16 -> clean 15 (dup=1, invalid emails=1)
Segments: {A - Priority=11, B - Warm=3, C - Needs enrichment=1}
By region: {DACH=11, Benelux=2, Other=2}
Avg score 85.67 | top: Nordwind Handel GmbH (100)
Configuration (config/scoring.properties)
weight.company=15
weight.email=25
weight.regionFit=5
segment.a=80
segment.b=55
target.regions=DACHRaise weight.email for cold-email campaigns, or switch target.regions to focus another market — the scores and segments re-compute accordingly.
NormalizerTest · EmailValidatorTest · ScoringEngineTest (full DACH lead = 100, −25 without email, thresholds) · LeadPipelineTest (16 → 15 clean, 1 dup + 1 invalid dropped, segments 11/3/1, email-pattern derivation).
Sample project — the lead data is fictional/illustrative and clearly labeled. In a real engagement the engine runs on publicly sourced business data.