JavaFX TableView e ObservableList - Como atualizar automaticamente a tabela?

Sei que perguntas semelhantes a essa foram feitas e em datas diferentes, mas vou colocar um SSCCE aqui e tentar fazer isso de forma simples.

Gostaria de poder atualizar o modelo de dados e ter quaisquer visualizações atualizadas automaticamente, de forma que qualquer chamador que atualize o modelo não esteja ciente de quaisquer visualizações existentes atualmente. Isto é o que eu aprendi / tentei até agora, e sem chamarTableView.refresh() não atualiza. o que estou perdendo?

main.java:

package application;

import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;

public class Main extends Application {
    @Override
    public void start(Stage stage) {

        // data
        ObservableList<Crew> data = FXCollections.observableArrayList();
        data.addAll(new Crew(1, "A"), new Crew(2, "B"));

        // table
        TableColumn<Crew, Integer> crewIdCol = new TableColumn<Crew, Integer>("Crew ID");
        crewIdCol.setCellValueFactory(new PropertyValueFactory<Crew, Integer>("crewId"));
        crewIdCol.setMinWidth(120);
        TableColumn<Crew, String> crewNameCol = new TableColumn<Crew, String>("Crew Name");
        crewNameCol.setCellValueFactory(new PropertyValueFactory<Crew, String>("crewName"));
        crewNameCol.setMinWidth(180);
        TableView<Crew> table = new TableView<Crew>(data);
        table.getColumns().addAll(crewIdCol, crewNameCol);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        // button
        Button button = new Button(" test ");
        button.setOnAction(ae -> {
            // test
            StringProperty nameProp = data.get(0).crewName();
            if(nameProp.get().equals("A")) {
                data.get(0).setCrewName("foo");
                // table.refresh();
                System.out.println("foo");
            } else {
                data.get(0).setCrewName("A");
                // table.refresh();
                System.out.println("A");
            }
        });

        VBox box = new VBox(10);
        box.setAlignment(Pos.CENTER);;
        box.getChildren().addAll(table, button); 
        Scene scene = new Scene(box);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Crew.java

package application;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Crew {
    private final IntegerProperty crewId = new SimpleIntegerProperty();
    private final StringProperty crewName = new SimpleStringProperty();

    Crew(int id, String name) {
        crewId.set(id);
        crewName.set(name);
    }

    public IntegerProperty crewId() { return crewId; }
    public final int getCrewId() { return crewId.get(); }
    public final void setCrewId(int id) { crewId.set(id); }

    public StringProperty crewName() { return crewName; }
    public final String getCrewName() { return crewName.get(); }
    public final void setCrewName(String name) { crewName.set(name); }

}

questionAnswers(1)

yourAnswerToTheQuestion