In either case, pcb_board_t's most important field is pcb_data_t *data, which holds lists and vectors to store all objects on the board. When the target is a buffer (e.g. io plugin's ->parse_element function), it's a pcb_buffer_t *buf which also have a pcb_data_t *data containing the same data a board could.
A pcb_data_t struct in turn has three sets of important fields:
Any code that needs to create objects should use the functions in obj_*.h. The functions that operate layer-independently usually get a destination as pcb_data_t, so they can operate both on boards and buffers. A typical example is pcb_pstk_t *pcb_pstk_new(pcb_data_t *data, ...) that returns NULL or the pointer to the new via that is already added to the corresponding list of data and in the rtree.
Layer object creation is done referencing the layer as pcb_layer_t *. A typical example is pcb_line_t *pcb_line_new(pcb_layer_t *layer...) which returns NULL on error or a pointer to the new line object created (inserted in the layer's line list, added to the rtree).
Code should avoid manipulating the lists and rtree structures directly.
Figure 1. simplified map of object related data structures
diamond: variable; rectangle: struct; round: struct field; green: drawing primitive