4  A Complete DataRaft Workflow in 15 Minutes

This chapter runs the first complete workflow against the same synthetic policy data used throughout the book. It stays in memory so that the acceptance logic is visible before persistence is introduced.

4.1 Load the product data

library(dataraft)

policy_delivery <- read.csv("data/policies.csv")
policy_delivery
  policy_id broker_id premium  status
1         1        10 1200.50  active
2         2        20  950.25 paid_up
3         3        10  600.75  lapsed
4         4        30 1500.00  active

The table is ordinary R data. Nothing has been accepted yet.

4.2 Define the agreement

The first product states the interface and one business rule.

policy_contract <- dr_contract(
  "policies",
  columns = c(
    policy_id = "integer",
    broker_id = "integer",
    premium = "numeric",
    status = "character"
  ),
  key = "policy_id"
)

policies <- dr_product("policies") |>
  dr_add_contract(policy_contract) |>
  dr_add_quality(~ premium >= 0)

The product is a definition. The incoming rows remain a separate object.

4.3 Run a bad delivery

Create one known defect instead of waiting for a real source system to surprise us.

bad_delivery <- policy_delivery
bad_delivery$premium[2] <- -20

bad <- dr_run(
  policies,
  data = bad_delivery,
  write = FALSE,
  stop_on_failure = FALSE
)

bad$status
[1] "blocked"
dr_quality_report(bad)
# A tibble: 9 × 7
  stage     rule               status n_failed n_total failure_rate message
  <chr>     <chr>              <chr>     <dbl>   <dbl>        <dbl> <chr>  
1 candidate schema             passed        0       1         0    ""     
2 candidate types              passed        0       4         0    ""     
3 candidate nonempty           passed        0       1         0    ""     
4 candidate not_null:policy_id passed        0       4         0    ""     
5 candidate not_null:broker_id passed        0       4         0    ""     
6 candidate not_null:premium   passed        0       4         0    ""     
7 candidate not_null:status    passed        0       4         0    ""     
8 candidate unique_key         passed        0       4         0    ""     
9 candidate premium >= 0       failed        1       4         0.25 ""     
dataraft.core::dr_quality_rows(bad)
# A tibble: 1 × 4
  policy_id broker_id premium status 
      <int>     <int>   <dbl> <chr>  
1         2        20     -20 paid_up

write = FALSE skips the configured DataRaft writer. stop_on_failure = FALSE returns the blocked result so the evidence remains inspectable.

The useful result is not only “the code failed.” The useful result identifies the acceptance rule that failed and, for a row predicate, the rows involved.

4.4 Correct and collect

Run the unchanged product definition against the valid fixture.

good <- dr_run(
  policies,
  data = policy_delivery,
  write = FALSE
)

good$status
[1] "completed"
accepted <- dr_collect(good)
accepted
# A tibble: 4 × 4
  policy_id broker_id premium status 
      <int>     <int>   <dbl> <chr>  
1         1        10   1200. active 
2         2        20    950. paid_up
3         3        10    601. lapsed 
4         4        30   1500  active 

dr_collect() accepts executed results, not product definitions. That distinction creates a small trust boundary: downstream code asks for data only after the delivery has passed the configured gate.

4.5 Read the result as evidence

A run result carries more than rows. It records status, validation status, timestamps, inputs, outputs, warnings, quality evidence, and an inspected copy of the definition.

good$validation_status
[1] "passed"
good$run_id
[1] "rb885edb00560eb6c06fa0387"
names(good$metadata)
 [1] "product"           "column_lineage"    "transformations"  
 [4] "schema"            "rows"              "contract"         
 [7] "lineage"           "submitted_rows"    "run_id"           
[10] "product"           "definition"        "policies"         
[13] "code_version"      "started_at"        "finished_at"      
[16] "status"            "backend"           "validation_status"
[19] "quality"           "inputs"            "outputs"          
[22] "port_outputs"     

For repeated work, keep the result object before collecting:

result <- dr_run(
  policies,
  data = incoming_delivery,
  write = FALSE,
  stop_on_failure = FALSE
)

if (result$status == "completed") {
  accepted <- dr_collect(result)
}

The placeholder incoming_delivery represents the next delivery in your own process. The executable examples above use the checked-in fixture.

WarningA successful R expression is not an accepted delivery

The important status is the DataRaft run status and its checks, not whether the R process reached the end of the script.

4.6 Why not publish yet?

Publication answers a second question: where should accepted data live? Keeping this chapter in memory lets you learn the acceptance boundary independently from filesystem, database, or lake semantics.

Chapter 9 will attach a versioned RDS target. The contract and quality rule do not need to change.

4.7 Exercises

  1. Change one premium to a negative value and compare status, the quality report, and affected rows.
  2. Remove premium from the delivery. Explain why that is an interface failure rather than the same kind of failure as a negative premium.
  3. Duplicate a policy_id. Which expectation detects the problem, and why is identity stronger than a column name?